Let's say you have a very long method, like this:
int monster()
{
int rc = 0;
// some statements ...
if (dragonSlayer.on_vacation()) {
cout << "We are screwed!\n";
if (callTheKing() == true)
return 1;
else
return 2;
} else {
cout << "We are saved!\n";
slayTheDragon();
}
// rest of long method...
return rc;
}
and I'm working on skeletonizing the code. I want to extract the dragon slaying part to
int handleDragon() {
if (dragonSlayer.on_vacation()) {
cout << "We are screwed!\n";
if (callTheKing() == true)
return 1;
else
return 2;
} else {
cout << "We are saved!\n";
slayTheDragon();
}
return 0; // ?
}
and replace the code in monster() with a call to handleDragon().
But there is a problem. There is a return statement in the middle of that part. If I keep the part where the return code of handleDragon() is handled, it will keep the litter in the big method.
Besides using exceptions, is there an elegant and safe way to refactor this piece of code out of the monster method? How should these types of situations be handled?
Return 0 from the handleDragon method if the dragon slayer is available:
int handleDragon() {
if (dragonSlayer.on_vacation()) {
cout << "We are screwed!\n";
if (callTheKing() == true)
return 1;
else
return 2;
} else {
cout << "We are saved!\n";
slayTheDragon();
return 0;
}
}
Then back in the monster method, if the return value was greater than zero, return that value, otherwise carry on:
// some statements ...
int handleDragonResult = handleDragon();
if (handleDragonResult > 0) {
return handleDragonResult;
}
// rest of long method...
You should also document the handleDragon method, to explain the value that gets returned.
enum DragonHandled { DHSuccess, DHKing, DHNoKing };
inline DragonHandled askForKing()
{
if (callTheKing())
return DHKing;
else
return DHNoKing;
}
DragonHandled handleDragon()
{
if (dragonSlayer.on_vacation()) {
cout << "We are screwed!\n";
return askForKing();
}
cout << "We are saved!\n";
slayTheDragon();
return DHSuccess;
}
int monster()
{
some_statements(...);
DragonHandled handled = handleDragon();
if( handled != DHSuccess )
return handled; // enum to int is an implicit cast
return more_statements(...);
}
Except for a function that returns an actual signed number, I would not return int. If the result has a meaning, define that meaning properly (that is: an enum).
A function does something, and whatever it does, should be visible in its name. So there should be a verb in a function's name (handledragon(), callTheKing()). monsters isn't a verb, it isn't something you can do. If I see an identifier monsters, I'd think it's a container for monsters.
Checking if(x == true) is just useless noise, since if(x) is terser, simpler and just as true.
Couldn't you do this:
int handleDragon() {
int rc = 0;
if (dragonSlayer.on_vacation()) {
cout << "We are screwed!\n";
if (callTheKing() == true)
rc = 1;
else
rc = 2;
} else {
cout << "We are saved!\n";
slayTheDragon();
}
return rc;
}
and then:
int monster()
{
int rc = 0;
// some statements ...
rc = handleDragon();
// rest of long method...
return rc;
}
or if you want to do something with the return code:
int monster()
{
int rc = 0;
// some statements ...
int handleDragonReturnCode = handleDragon();
if(handleDragonReturnCode == 0) {
// do something
}
else {
// do something else
}
// rest of long method...
return rc;
}
Is this what you want? On a general note, avoid using magic numbers like 1 and 2 for your return codes. Use constants, #define, or enum.
Concerning return, try to have one exit point from your function. As you have found out, having multiple return statements can make refactoring hard (as well as understanding the logic unless it's really simply).
The question was about the strategy so I think the answer by Richard Fearn is a good one.
To make it into a refactoring pattern it would look something like:
Context: A section in the middle of a larger method is to be extracted.
Problem: The section contains return statements.
Solution:
Extract the code to a new method returning the same type as the larger method.
Find a value of that type that does not mean anything. Call that value CONTINUE.
Add a statement at the end of the new method that returns CONTINUE.
In the larger method test the return value from the new method for CONTINUE. If it is not then return that value.
This would be the principal approach. As the next step you could refactor the return values from the new method to something more meaningful (like in the answer from sbi). And you'd have to find a way to handle the case where the return type isn't a scalar or simple type, returning a NULL object or some such.
Related
I was wondering what I may have done wrong in writing this simple function which is supposed to return true if the given number is a prime, or false if not a prime.
bool isPrime(int num)
{
if (num <= 1)
{
status = false;
}
else
{
for (int i = 1; i <= num; i++)
{
if (num % i == 0)
{
dividers++;
}
}
if (dividers == 2)
{
status = true;
}
else
{
status = false;
}
}
return status;
}
Obviously, my main looks like this:
bool isPrime(int num);
bool status;
int dividers = 0;
int main() {
isPrime(2);
if (!isPrime)
{
std::cout << "Not prime" << std::endl;
}
else
{
std::cout << "Prime" << std::endl;
}
return 0;
}
I'm a C++ beginner and I'd really appreciate it if someone could help me there and correct my logic.
Have a good day:)
The immediate problem is in this two lines:
isPrime(2);
if (!isPrime)
The first line calls the function and discards the returned value. The second line converts a pointer to the function to bool. The output of your code does not depend on what you actually do in isPrime.
That is not how you call a function and use its result!
Instead you want
if (isPrime(2)) {
or
bool isP = isPrime(2);
if (isP) { ...
As mentioned in comments, there are also problems in the implementation of isPrime, but I hope this is enough to set you back on the right track.
PS: You should get rid of the global variable status. You do not need both, the return value and a global that stores the result, and if you can choose, you should definitely go for the return value.
checkUsername() checks the username's length, and returns true when length is greater than or equal to 5. Otherwise it returns false.
The function checkUsername() should return false on BadLengthException(), but it doesn't seem to appear as none of the code within checkUsername() and BadLengthException::what() returns false. But still the program is working fine when it encounters a username of length less than 5. What's going on here? How is the return value passed false?
class BadLengthException: public exception{
public:
int n;
BadLengthException(int x) { n=x; };
virtual int what() throw() {
return n;
}
};
/*
This function checks the username's length,
and returns true when length is greater than or equal to 5.
Otherwise it returns false.
*/
bool checkUsername(string username) {
bool isValid = true;
int n = username.length();
if(n < 5) {
throw BadLengthException(n); //the problem
}
for(int i = 0; i < n-1; i++) {
if(username[i] == 'w' && username[i+1] == 'w') {
isValid = false;
}
}
return isValid;
}
int main() {
int T; cin >> T;
while(T--) {
string username;
cin >> username;
try {
bool isValid = checkUsername(username);
if(isValid) {
cout << "Valid" << '\n';
} else {
cout << "Invalid" << '\n';
}
} catch (BadLengthException e) {
cout << "Too short: " << e.what() << '\n';
}
}
return 0;
}
A function can either return a value or throw an exception, it can't do both, they're mutually exclusive. If it successfully returns a value that means the code didn't throw an exception, and if an exception was thrown then it means it didn't make it to the point of returning a value.
Further to that, capturing the return value is also interrupted, the code jumps right to the catch block you've defined. It's like a hard goto in concept, if you ignore things like automatic object destruction and finally type implementations which will happen in the process of an exception bubbling up.
When the exception is thrown in checkUsername(), it stops processing in that function and returns to the calling function which is main(). Because the call was made in a try block the exception is handled by the catch block.
The if() statement is completely ignored and the catch doesn't care about what happened in that function and just prints "Too short: "
I am writing two functions in a program to check if a string has an assigned numeric code to its structure array or if the given numeric code has an assigned string in the same structure array. Basically, if I only know one of the two, I can get the other. I wrote the following:
int PrimaryIndex::check_title_pos(std::string title) {
bool findPos = true;
if (findPos) {
for (int s = 1; s <= 25; s++) {
if (my_list[s].title == title) {
return s;
}
}
} else {
return -1;
}
}
std::string PrimaryIndex::check_title_at_pos(int pos) {
bool findTitle = true;
if (findTitle) {
for (int p = 1; p <= 25; p++) {
if (my_list[p].tag == pos) {
return my_list[p].title;
}
}
} else {
return "No title retrievable from " + pos;
}
}
However, it says not all control paths have a return value. I thought the else {} statement would handle that but it's not. Likewise, I added default "return -1;" and "return "";" to the appropriate functions handling int and string, respectively. That just caused it to error out.
Any idea on how I can keep this code, as I'd like to think it works but cant test it, while giving my compiler happiness? I realize through other searches that it sees conditions that could otherwise end in no returning values but theoretically, if I am right, it should work fine. :|
Thanks
In the below snippet, if s iterates to 26 without the inner if ever evaluating to true then a return statement is never reached.
if (findPos) {
for (int s = 1; s <= 25; s++) {
if (my_list[s].title == title) {
return s;
}
}
}
I don't really understand the system. Using .RegisterDirect("myFunc",myFunc) I can register a function that can't take parameters, but therefore can return a value.
Using .Register("myFunc",myFunc) I can register a function that can take parameters, but therefore can't return any values.
I literally spent days on this issue now and I just can't figure it out. I would really appreciate it if someone would take a look at this.
Here is the documentation. Here is a quick example of how Register and RegisterDirect. Let's say I wanted the Print function to always return the string "hello". How would I do that?
#include "stdafx.hpp"
int Print(LuaPlus::LuaState* pState) {
int top = pState->GetTop();
std::stringstream output;
for( int i = 1; i <= top; ++i ) {
output << pState->CheckString(i) << std::endl;
}
std::cout << output.str();
return 0; // We don't return any values to the script
}
int Get2222() {
return 2222;
}
int main() {
LuaPlus::LuaState* pState = LuaPlus::LuaState::Create( true );
LuaPlus::LuaObject globals = pState->GetGlobals();
globals.Register("Print",Print);
globals.RegisterDirect("Get2222",Get2222);
char pPath[ MAX_PATH ];
GetCurrentDirectory(MAX_PATH,pPath);
strcat_s(pPath,MAX_PATH,"\\test.lua");
if( pState->DoFile(pPath) ) {
// An error occured
if( pState->GetTop() == 1 )
std::cout << "An error occured: " << pState->CheckString(1) << std::endl;
}
LuaPlus::LuaState::Destroy( pState );
pState = nullptr;
getchar();
return 0;
}
Afaik I would have to push the value on the stack and return 1 to indicate that there is something on the stack. But that didn't seem work. I tried to add this to the function to return the number 4:
pState->PushInteger(4);
return 1;
I really hope you can help me out here.
I implemented a Quiz Code and did a short change at the end of it to check if the User answered it correctly.
My if / else looks like this:
if (answer == rightanswer){
rightA = true;
}
else {
rightA = false;
}
return rightA;
I already checked with the debugger that if the correct answer is entered it goes to rightA = true; and to return, so this works finde.
But if i check the value of rightA it's false.
If it's needed, here is the function that i use to call the Quiz:
void gameOver(char field[HEIGHT][WIDTH], char newField[HEIGHT][WIDTH]){ // TODO
bool rightA = false;
showQuizDialog(rightA);
do{
system("cmd /c cls");
switch (rightA){
case true : cout << "menu"; menu(field, newField); break;
case false : showQuizDialog(rightA); break;
default : cout << " ";
}
}while(rightA == false);
}
I'm a bit hintless. I may have some logic failure in it i just don't see at the moment.
Greetings
E: I don't wanted to bomb you guys with code. But here is it:
bool showQuizDialog(bool rightA){
Quiz* quiz = Quiz::getInstance();
quiz -> askQuestion(rightA);
return rightA;
}
And the full askQuestion:
bool Quiz::askQuestion(bool rightA) {
int fragenID = rand() % this->fragen.size(); //zufällige Fragen auswählen
struct Question frage = this->fragen.at(fragenID);
std::cout << frage.frage.c_str() << std::endl << endl; //Frage stellen
int rightanswer = this->listAnswers(frage.antworten);
int answer = this->readAnswer(0, frage.antworten.size() - 1);
if (answer == rightanswer){
rightA = true;
}
else {
rightA = false;
}
return rightA;
}
Is showQuizDialog(rightA) supposed to magically change the value of rightA? (I'm assuming you're not passing it by reference).
Did you mean to write rightA = showQuizDialog(rightA) or rightA = quiz -> askQuestion(rightA)?
Also, in your switch that switches on a bool, do you expect any other values than a true or a false?
Your showQuizDIalog is a call-by-value function. So always store the return value of the function into rightA, when calling showQuizDialog, that is :
rightA = showQuizDialog(rightA);
Otherwise, change your function declaration to allow pass-by-reference, maybe like this
showQuizDialog(&rightA);
and no need to return anything from the function(just use a pointer instead of a variable rightA inside the function)