user3677061
user3677061

Reputation: 65

Is there a way I can make a function that is supposed to return a value not?

I've come up with a block of code that performs an essential function in my program, but also screws up a bunch of other functions. The problem would be solved if it didn't return a null item when no matching term is found using find_if and instead it just printed a message and the program continued on. However, string_to_item must return an item because it is called as an argument in a function that requires an item and any attempt to remove return items("null") causes a run time error. My question is, is there any way I can have this function not return an item if the vector doesn't have an element matching string item_name, or do I need to rewrite the involved functions? The code is:

items string_to_item( string item_name, vector<items>& item_container ) {
    struct comparer {
        comparer( string const& item_name) 
            : item_name(item_name) {}
        bool operator()(items const& it) const { return it.name == item_name; }
        string const& item_name;
    }

    comp(item_name);

    vector<items>::iterator result = find_if(item_container.begin(), item_container.end(), comp);
    int i = distance( item_container.begin(), result);
    if ( result == item_container.end() ) {
        if( item_container.size() == 0 ) {
            cout << "no elements in vector  ( get_item )";
        }
        else {
            cout << "You don't see that here";
            return items("null");
        }
        }

    return item_container[i];
}

Thanks!

Upvotes: 1

Views: 188

Answers (6)

There are a number of approaches you can use (which is not to say that you should use them):

  1. Return a pointer, returning 0 instead of a valid pointer when there's nothing to return.

    This is old C style which makes for pretty seamless use, but at the cost of the danger of segfaults. However, segfaults are quite easy to debug. Seamless use means, that you can use the implicit conversion of pointers to booleans to your advantage, writing code like:

     if(Foo* myFoo = getFoo()) {
         //do something with myFoo
     } else {
         //handle failure
     }
    
  2. Throw an exception when there's nothing to return.

    This is more like C++ style, but not an inch more safe than the old C style. An uncaught exception is generally less easy to debug than a segfault because it is not necessarily easy to see where the exception came from.

  3. Use some other kind of sentinel value.

    Again, this does not avoid the fact that the caller may forget to correctly handle failure. So this is more or less a receipt for disaster.

  4. Use two return values: one with the value, and one with a flag indicating whether the return value is valid. You can use a throw-away struct for this. Or use boost::optional<>, which is a glorified throw-away struct for this purpose.

    Again, this is a receipt for disaster since the calling code is not forced to handle failure.

  5. Pass in a default value to return when there is nothing to return.

    This has the advantage of letting the caller decide how precisely failure should be handled, but requires an additional argument. Generally not worth the effort.

So the bottom line is: You can't do much better than just returning a pointer which may be 0.

Upvotes: 1

Jarod42
Jarod42

Reputation: 217245

If you don't change the signature, you may throw an exception.

Else, if you may change the return value:

Upvotes: 1

user3416290
user3416290

Reputation:

If you don't want to return a null value if no matches between string item_name and vector& item_container are found, you could return a string with value "null", something with value 0 or something similar that you can later handle from other functions and doesn't give you any problems

Upvotes: 0

TNA
TNA

Reputation: 2745

To optionally return an element, boost::optional<Item> is a good solution. "optional" will also be part of C++17.

Upvotes: 0

Frerich Raabe
Frerich Raabe

Reputation: 94319

The only solution I can think of is to pass a function pointer (or a functor) to string_to_item which references the code to be executed in case no matching item is found, as with CPS.

However, you really don't want to "not return a value" here. I think the better fix is to adjust the callers of your function.

For what it's worth, printing a message to cout in the middle of such a low-level function seems fairly questionable, too.

Upvotes: 2

Luchian Grigore
Luchian Grigore

Reputation: 258588

The idiomatic approach would be to return the iterator itself, and then compare against item_container.end() in the calling site.

You can't make a function conditionally have or not have a return value.

Upvotes: 2

Related Questions