user14503825
user14503825

Reputation:

Is there a more C++ elegant implementation to complete the function jump?

In my daily work, I usually write my code like this:

int ret = 0;
ret = func1();
if(ret != 0) {
  return ret;
}
ret = func2();
if(ret != 0) {
  return ret;
}

But this means I need to complete a lot of "if(ret!=0){return ret;}",Is there a more C++ elegant implementation to complete the function jump? By the way, we are not allowed to use exception.

Upvotes: 4

Views: 221

Answers (5)

Damien
Damien

Reputation: 4864

Another possibility is to perform a for range loop over a set of functions:

for (auto& ff: {func1, func2, func3}) {
    

Output:

in func1
in func2
in func3
3

Code:

#include <iostream>

int func1() {
    std::cout << "in func1\n";
    return 0;
}
int func2() {
    std::cout << "in func2\n";
    return 0;
}
int func3() {
    std::cout << "in func3\n";
    return 3;
}
int foo () {
    for (auto& ff: {func1, func2, func3}) {
        int ret;
        if ((ret=ff()) != 0) {
            return ret;
        }
    }
    return 0;
}

int main() {
    std::cout << foo() << "\n";
}

In a comment, you asked the question on how to make it work with member functions. It seems more tricky, in particular because the compiler has difficulty to guess the proper type of the initializer_list. This is one way to solve it.

#include <iostream>

class Obj {
    public:
        int func1() {
            std::cout << "in func1\n";
            return 0;
        }
        int func2() {
            std::cout << "in func2\n";
            return 0;
        }
        int func3() {
            std::cout << "in func3\n";
            return 3;
        }
        int foo () {
            for (auto ff: {&func1, &func2, &func3}) {
                int ret;
                if ((ret=(this->*ff)()) != 0) {
                    return ret;
                }
            }
            return 0;
        }
};

int main() {
    Obj var;
    std::cout << var.foo() << "\n";
}

Upvotes: 2

jfMR
jfMR

Reputation: 24758

By using a fold expression and an if statement with initialization you can write the following variadic function template that takes an arbitrary number of pointers to such functions as non-type template arguments:

template<int(...Funcs)()> // equivalent to template<int(*...Funcs)()>
int overall_func() {
   if (int ret; (... || (ret = Funcs())))
      return ret;
   return 0;
}

This way, you factor out all the calls to those functions, the corresponding return value assignments, and their individual checks. Eventually, it boils down to a single check and a single return statement:

if (int ret = overall_func<func1, func2, func3>(); ret)
   return ret;

This is regardless of the number of functions to call.

Upvotes: 2

RedFog
RedFog

Reputation: 1015

#include <iostream>
#include <functional>

struct Status{
    int status;

    template<typename Func, typename... Args>
    Status& invoke(Func&& func, Args&&... args){
        if (!status)
            status = std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
        return *this;
    };
    int value() const noexcept{
        return status;
    }
};

int func1(){
    std::cout << "call func 1" << std::endl;
    return 0;
}
int func2(int a){
    std::cout << "call func 2 with " << a << std::endl;
    return a;
}
int func3(float x){
    std::cout << "call func 3 with " << x << std::endl;
    return 1;
}

int foo(){
    std::cout << "call foo" << std::endl;
    return Status{}.invoke(func1).invoke(func2, 0).invoke(func3, 0).value();
}

int bar(){
    std::cout << "call bar" << std::endl;
    return Status{}.invoke(func2, 3).invoke(func1).invoke(func3, 0).value();
}

int main(){
    int a;
    a = foo();
    std::cout << "foo return " << a << std::endl;
    a = bar();
    std::cout << "bar return " << a << std::endl;
    return 0;
}

it shows:

call foo
call func 1
call func 2 with 0
call func 3 with 0
foo return 1
call bar
call func 2 with 3
bar return 3

I don't think it's the best way to do that, but it's elegant enough, in my mind. you can serialize all functions and apply short-circuit evaluation (that's why invoke is needed). or you can write it as a command style:

int foo(){
    return Status{}
        .invoke(func1)
        .invoke(func2, 0)
        .invoke(func3, 3)
    .value();
}

by the way, if you want to know more about this, you can refer to Monad in Haskell.

Upvotes: 2

paxdiablo
paxdiablo

Reputation: 881653

C++, as a descendant of C, has the same short-hand method for assignment and checking together:

int ret;
if ((ret = func1()) != 0) return ret;
if ((ret = func2()) != 0) return ret;

This works because a = something is itself an expression whose value is a.

If you wanted to type even less code for each call-and-check, you could opt for a macro, but that's probably overkill (I wouldn't do it myself), but I'll show it just in case you're interested:

#define RNZ(x) if ((ret = x) != 0) return ret
int ret;
RNZ(func1());
RNZ(func2());

Upvotes: 1

Sam Varshavchik
Sam Varshavchik

Reputation: 118352

The shown code seems to be logically equivalent to:

int ret;

if ((ret=func1()) || (ret=func2()))
    return ret;

Tack on as many additional function calls as you wish, in the obvious manner.

Upvotes: 5

Related Questions