5

I have member function that looks like:

Point find_the_special_point(Image img);

However, this function may fail to find the point (for example the img does not contain a special point at all).

What is the right way to make the function return in some way that the point was not found?

I thought of some solutions but not sure which one the best or if there is a better approach:

1. Throwing exception if the point is not found:

I think it is just stupid because exceptions are not mean to control the flow. Furthermore, not finding a point is an expected behavior not an exception.

2. Alter the declaration to:

Bool find_the_special_point(Image img , Point& pnt);

Although it seems correct way, I think (not sure) that it is not so good to return the actual output via arguments.

3. Wrapping the out put:

struct find_the_special_point_result{
    bool is found;
    Point special_point;
};
find_the_special_point_result find_the_special_point(Image img);

It seems to be nice solution. However, I suspect that it is a bit overkill.

4. Returning pointer instead:

std::shared_ptr<Point> find_the_special_point(Image img);

and if it is not found return nullptr. However, I think dynamic allocation was not made for this case. It is kind of using the wrong tool to achieve another goal.

What is the convention way to solve this issue?

  • 3
    Option 3 is what std::map::insert does (the struct is std::pair<iterator, bool>), or std::optional<Point> if you have a C++17 compiler – Caleth Feb 23 '18 at 13:47
  • 2
    Option 1 is not stupid depending on what is the expected behaviour of your function. Should the Image always have a special point? If it should, then throwing an exception is fine. – Vincent Savard Feb 23 '18 at 13:58
  • @VincentSavard it may and may not have, I think I have updated the question after you load it on your browser! – Humam Helfawi Feb 23 '18 at 14:00
4

In C++, this question is not trivial because of object lifetimes and constructors/destructors. That doesn't matter for POD types. But it is relevant for other types, and crucially important when writing templated code.

If you return by value (even if wrapped in a struct or in a tuple), you will always have to instantiate this object. This may not be feasible for objects that acquire a resource in their constructor, or that are not default-constructible.

If you return by output-parameter, this is even worse. The caller must instantiate an empty object, and you must copy the return value into that object. This means the object must have an “empty” state, and be mutable. This empty state may be at odds with RAII, and makes the code more fragile.

The three robust solutions are:

  • use exceptions,
  • use an optional<T> type, or
  • return by pointer.

These are all very good, but have different tradeoffs.

Exceptions allow you to write your code as if the return value will always be there. However, C++ exceptions are unsuitable for a kind of return code. Only use them if the output will usually be there, and its absence is an error of some kind. E.g. std::vector::at() uses exceptions when no result can be returned because a bounds check failed.

An optional type lets you return by value, but the value may be uninitialized. This sidesteps the problems mentioned above regarding construction of the returned object. An optional type can be thought of as a type-safe tagged union with a single member.

Returning by a pointer-like type is the most general solution, because this can handle objects that cannot be copied or moved. By using smart pointers, you can specify ownership semantics (unique, shared, or plain pointers for unspecified/borrowed). This is done very commonly with iterator-based algorithms, as iterators are somewhat pointer-like. The advantage of using types that have a boolean conversion is that you can easily check the return value in a conditional:

if (auto result = some_function()) {
  result->foo();  // statically known that result != nullptr
}

Pointers or references are necessary anyway when returning polymorphic types, so using pointers might not be a big change.

The disadvantage is of course when you create a new object that you want the caller to own. You should then return an unique_ptr. But if you would otherwise return by value, returning an optional would be better, as it avoids unnecessary indirection and heap allocations.

| improve this answer | |
  • Thank you very much! This answer has enlightened me much beyond my question scope. – Humam Helfawi Feb 24 '18 at 19:24
3

Returning something if that something is there, and nothing otherwise, is exactly what std::optional is made for.

If your compiler doesn't yet have this type and you can't upgrade, you can use the Boost version.

| improve this answer | |
1

I would argue that option #2 is the way to go. As you stated in Option #1, exceptions aren't meant to control flow of the program; especially if failure is an expected option. (which it is in your case)

Booleans are, and they're used for it frequently. ie: if(flag) #do something else #do other thing

| improve this answer | |
-7

1. Throwing exception if the point is not found:

This is dangerous because exceptions are gotos in disguise. It takes experience with them to get them correct. If you are writing this for others, then I recommend you avoid this option.

2. Alter the declaration to:

A viable option but other developers will ignore the bool.

3. Wrapping the out put:

Same as #2; the bool part is likely to be ignored. Also, perhaps you would want to use and object instead of a struct.

4. Returning pointer instead:

A combination of the above. Most developers will simply assume that nullptr will never be returned.


A fifth option: 5. Return a guard value.

Since the point is in an image, its coordinates are always zero or positive. Returning a guard value, say (-1,-1), is common but can cause errors later in the code since the guard value too will most likely be ignored.


There is no good answer to this. All options have problems. The best you can do is to follow the most common practice of those who will be using the function.

| improve this answer | |
  • 3
    I strongly disagree with "exceptions are gotos in disguise". They are a clean way to tell your caller that you couldn't do your job, and by default protect him from doing any consecutive computations based on wrong or missing data. – Ralf Kleberhoff Feb 23 '18 at 15:44
  • 2
    And your fifth option reminds me of the bad old times with Kernighan&Ritchie C in the 1970s. – Ralf Kleberhoff Feb 23 '18 at 15:45
  • 1
    Stop assuming your colleagues are idiots. If they misuse your function, then the contract wasn't as clear as you thought it was, which is exactly the point of this question. – Vincent Savard Feb 23 '18 at 15:46
  • @VincentSavard I'm not assuming anything. But have have seen very experienced programmers make simple mistakes because of tunnel vision. They're so focus the difficult part that they overlook the simple. – shawnhcorey Feb 23 '18 at 21:00
  • 1
    @RalfKleberhoff If exceptions just returned to the caller, then yes. But they don't. If the caller ignores them, then any function in the call tree can intercept them, thus making them gotos. – shawnhcorey Feb 23 '18 at 21:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.