Search code examples
c++returnc++17

Test return value and return


This may be a silly question, but I am working if there is a concise way to test the return result of a function, and, if it fails to meet a condition, return that value (i.e., pass it on).

To address a possible question right now, yes, what I am looking for is similar to what exceptions offer. However, as an embedded developer, the overhead associated with exceptions (unfortunately) is too high a cost to pay.

For example,

enum class Status
{
    OKAY,
    ERROR1,
    ERROR2,
    ERROR3
};

Status foo1();
Status foo2();
Status foo3();

Status bar()
{
    Status result;

    result = foo1();
    if (result != Status::OKAY) return result;
   
    result = foo2();
    if (result != Status::OKAY) return result;

    result = foo3();
    if (result != Status::OKAY) return result;

    return Status::OKAY;
}

If I was just using a bool as the return value, a shorthand would be to write the following:

bool bar()
{
    if (!foo1()) return false;
    if (!foo2()) return false;
    if (!foo3()) return false;

    return true;
}

A side-effecting if clause like this is considered a code smell, but this seems more readable and concise to me than local variable assignments and tests.

Is there a a shorthand way to accomplish the same readability as the second example but with the same effect as the first example?


Solution

  • Since you're using C++17, you could write your bar() function like this, which isn't too bad, IMO:

    Status bar() {
        if (auto result = foo1(); result != Status::OKAY) return result;
        if (auto result = foo2(); result != Status::OKAY) return result;
        if (auto result = foo3(); result != Status::OKAY) return result;
        return Status::OKAY;
    }
    

    Or, a lambda could come in handy as well. Although, I find it less appealing in this particular situation:

    Status bar() {
        auto result = Status::OKAY;
        auto const check = [&result](auto foo) -> bool
        { return (result = foo()) != Status::OKAY; };
    
        if (check(foo1)) return result;
        if (check(foo2)) return result;
        if (check(foo3)) return result;
        return result;
    }
    

    Maybe writing your own functor works better:

    Status bar() {
        struct {
            Status result;
            bool operator()(Status (&foo)())
            { return (result = foo()) != Status::OKAY; };
        } check{};
    
        if (check(foo1)) return check.result;
        if (check(foo2)) return check.result;
        if (check(foo3)) return check.result;
        return check.result;
    }
    

    Another way, is to take advantage of aggregate initialization, but this is stretching it a bit in terms of trickery.

    Status bar() {
        struct convert {
            Status result;
            explicit operator bool() const
            { return result != Status::OKAY; }
        } check{};
    
        if ((check = convert{foo1()})) return check.result;
        if ((check = convert{foo2()})) return check.result;
        if ((check = convert{foo3()})) return check.result;
        return check.result;
    }
    

    Live example