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