Can this get to the end of the function and not return anything? - c++

I am making a c++ text based game and while making a feature for eating food I made this function names eating. I am getting a warning saying its possible to reach the end with no return value. How is it possible to get to the end and not return something?
int inventory::eat(std::string eating)
{
if (!consumables.empty())
{
for (int i = 0; i < consumables.size(); i++)
{
if (consumables[i].name == eating)
{
return consumables[i].effect;
}
else
{
return 404;
}
}
}
else
{
return 505;
}
}

Presumably the compiler is unable to tell that if consumables.empty() == false, then consumables.size() > 0.
I'd probably rewrite it as this, but I'm worried about the if/else inside your for loop.
int inventory::eat(std::string eating) {
for (int i = 0; i < consumables.size(); i++) {
if (consumables[i].name == eating) {
return consumables[i].effect;
} else {
return 404;
}
}
return 505;
}

Assuming no other thread modifies consumables while eat() is running, then no, return will not be skipped. But the compiler doesn't know that. It doesn't knower that !empty() and size() > 0 mean the same thing for a container. They are just two separate method calls.
That said, I would suggest writing the code more like this, which is easier to read, and avoids the warning:
int inventory::eat(const std::string &eating)
{
if (consumables.empty())
return 505;
for (int i = 0; i < consumables.size(); ++i)
{
if (consumables[i].name == eating)
return consumables[i].effect;
}
return 404;
}
Or, if you are using C++11 and consumables supports iterators:
#include <algorithm>
int inventory::eat(const std::string &eating)
{
if (consumables.empty())
return 505;
auto found = std::find_if(
consumables.begin(), consumables.end(),
// replace 'consumable' below with your actual type name as needed...
[&](const consumable &c){ return c.name == eating; }
);
if (found != consumables.end())
return found->effect;
return 404;
}

Related

Error: control may reach end of non-void function in C++

I cannot figure out why this error is happening: error: "control may reach end of non-void function" even when "else" statement is present at the end.
Here is the code:
bnode* binsert(bnode *h,int k){
bnode *temp=new bnode;
if(h==NULL)
{
temp->num=k;
temp->L=NULL;
temp->R=NULL;
h=temp;
return h;
}
else if(h->L==NULL && k<h->num)
{
temp->num=k;
temp->L=NULL;
temp->R=NULL;
h->L=temp;
return h;
}
else if(h->R==NULL && k>h->num)
{
temp->num=k;
temp->L=NULL;
temp->R=NULL;
h->R=temp;
return h;
}
else if(h->L!=NULL && k<h->num)
{
h->L=binsert(h->L,k);
}
else
{
h->R=binsert(h->R,k);
}
}
You need to return the results of recursive calls, it's not done automatically.
You can also simplify your code a bit by adding a constructor:
bnode::bnode(int v)
: num(v),
L(nullptr),
R(nullptr)
{
}
and since you're already handling the case of a null parameter, you don't need special cases for null children:
bnode* binsert(bnode *h,int k)
{
if(h == nullptr)
{
h = new bnode(k);
}
else if(k < h->num)
{
h->L = binsert(h->L, k);
}
else if(k > h->num)
{
h->R = binsert(h->R, k);
}
return h;
}
because this last 2 conditions:
else if(h->L!=NULL && k<h->num)
{
h->L=binsert(h->L,k);
}
else
{
h->R=binsert(h->R,k);
}
may occur and no return is given...
you need to be sure the function returns a value no matter what the condition evaluates....
else if(h->L!=NULL && k<h->num)
{
h->L=binsert(h->L,k);
}
else
{
h->R=binsert(h->R,k);
}
In the else if and else cases for your code, if you reach here, you do not return a value, and the behavior is undefined if you try to use this value.
You probably want to add a return h; in the two branches.

Am I using function pointers correctly?

I have a function that looks something like this in pseudocode:
std::string option = "option1" // one of n options, user supplied
for (int i = 0; i < 100000; i++) {
if (option == "option1") {
doFunction1a();
} else if (option == "option2") {
doFunction2a();
} else if (option == "option3") {
doFunction3a();
}
// more code...
if (option == "option1") {
doFunction1b();
} else if (option == "option2") {
doFunction2b();
} else if (option == "option3") {
doFunction3b();
}
}
However, I could avoid the repeated if statement inside the loop by doing something like this:
std::string option = "option1" // one of n options, user supplied
int (*doFunctiona)(int, int);
int (*doFunctionb)(int, int);
if (option == "option1") {
doFunctiona = doFunction1a;
doFunctionb = doFunction1b;
} else if (option == "option2") {
doFunctiona = doFunction2a;
doFunctionb = doFunction2b;
} else if (option == "option3") {
doFunctiona = doFunction3a;
doFunctionb = doFunction3b;
}
for (int i = 0; i < 100000; i++) {
doFunctiona();
// more code...
doFunctionb();
}
I realize that this will have little effect on performance (the time spend by the functions dominates the time it takes to execute the if statement).
However, In terms of "good coding practices", is this a good way to set up variable function calling? With "good" I mean: (1) easily expandable, there could easily be 20 options in the future; 2) results in readable code. I'm hoping there exists some kind of standard method for accomplishing this. If not, feel free to close as opinion based.
Just use an unordered_map and spare yourself the if-else-if-orgy:
std::unordered_map<std::string, std::vector<int (*)(int, int)>> functions;
functions.insert({ "option1", { doFunction1a, doFunction1b } });
...
const auto& vec = functions["option1"];
for(auto& f : vec) f(1, 2);
Beside using map I recommend to use std::function and lambdas which will give you more flexibility and syntax is more friendly (at least for me):
std::unordered_map<std::string, std::function<void()>> functions {
{
"option1",
[] {
functionA();
functionB();
}
},
{
"option2",
[] {
functionC();
functionD();
}
}
};
auto optionFuncIt = functions.find("option1");
if (optionFuncIt != functions.end()) {
optionFuncIt->second();
} else {
std::cerr << "Invalid option name" << std::endl;
}

C++ successful `try` branch

Let's consider some artificial C++ code:
int i = 0;
try { someAction(); }
catch(SomeException &e) { i = -1; }
i = 1;
... // code that uses i
I want this code to assign -1 to i in case of someAction() throws exception and assign 1 in case if there was no exception. As you can see now this code is wrong because i finally always becomes 1. Sure, we can make some trick workarounds like:
int i = 0;
bool throwed = false;
try { someAction(); }
catch(SomeException &e) { throwed = true; }
i = throwed ? -1 : 1;
... // code that uses i
My question is: is there anything in C++ like "successful try branch", where I do some actions in case if there were no any throws in try block?
Something like:
int i = 0;
try { someAction(); }
catch(SomeException &e) { i = -1; }
nocatch { i = 1; }
... // code that uses i
Surely, there is no nocatch in C++ but maybe there is some common beautiful workaround?
int i = 0;
try { someAction(); i = 1; }
catch(SomeException &e) { i = -1; }
Aside from the simple solution
try
{
someAction();
i = 1;
}
catch(SomeException &e)
{
i = -1;
}
you should consider what you are planning to do with the value of i further in the code - use it in if statements? That is poor design, you could simply put all the code inside the braces after try and catch respectively.

Error: not all control paths return a value

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;
}
}
}

Return from a C++0x lambda caller directly

I've just rewritten the following C89 code, that returns from the current function:
// make sure the inode isn't open
{
size_t i;
for (i = 0; i < ARRAY_LEN(g_cpfs->htab); ++i)
{
struct Handle const *const handle = &g_cpfs->htab[i];
if (handle_valid(handle))
{
if (handle->ino == (*inode)->ino)
{
log_info("Inode "INO_FMT" is still open, delaying removal.",
(*inode)->ino);
return true;
}
}
}
}
With this C++0x STL/lambda hybrid:
std::for_each(g_cpfs->htab.begin(), g_cpfs->htab.end(), [inode](Handle const &handle) {
if (handle.valid()) {
if (handle.ino == inode->ino) {
log_info("Inode "INO_FMT" is still open, delaying removal.", inode->ino);
return true;
}
}});
Which generates:
1>e:\src\cpfs4\libcpfs\inode.cc(128): error C3499: a lambda that has been specified to have a void return type cannot return a value
I hadn't considered that the return in the lambda, doesn't actually return from the caller (having never seen a scoped function in C/C++ before now). How do I return true from the caller where the original function would have done so?
You don't; std::for_each isn't structured to handle an early return. You could throw an exception...
Or don't use a lambda:
for (auto const &handle : g_cpfs->htab) {
// code that was in lambda body
}
Use std::find_if() instead of std::for_each():
if (std::find_if(g_cpfs->htab.begin(), g_cpfs->htab.end(),
[inode](Handle const &handle) {
if (handle.valid() && handle.ino == inode->ino) {
log_info("Inode "INO_FMT" is still open, delaying removal.",
inode->ino);
return true;
}
return false;
}) != g_cpfs->htab.end()) {
return true;
}