Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding branching based on the return value of the function that is a template argument

Tags:

c++

c++11

Suppose the following policy classes that take care of one aspect of an algorithm:

struct VoidF {
    static void f() {
        ... // some code that has side effects
    }
};

struct BoolF {
    static bool f() {
        bool res = ...; // some computation
        return res;
    }
};

The BoolF policy is "enhancement-aware": when BoolF::f() returns true, the algorithm can exit. VoidF is "enhancement-unaware", hence it returns void (I don't want to force the user of my library to return bool when it does not mean anything to him).

The algorithm is currently written like this:

template <typename F>
struct Algorithm {
    void run() {
        ... // some computation here

        if (std::is_same<decltype(F::f()), bool>::value) {
            if (F::f()) return;
        } else
            F::f(); // If F is VoidF, there should be no branching and some
                    // compiler optimizations will be enabled

        ... // more computation, unless F::f() got rid of it
    }
};

Of course, this does not work if Algorithm is instantiated with VoidF. Is there a way to fix this in a way that there should be no branching in Algorithm<VoidF>::run() as indicated by the comment?

like image 962
AlwaysLearning Avatar asked Dec 09 '15 18:12

AlwaysLearning


2 Answers

Here is my own attempt to do it without SFINAE:

template <typename F>
struct Algorithm {
    void run() {
        ... // some computation here

        myRun(std::integral_constant<
              bool, std::is_same<decltype(F::f()), bool>::value>());
    }

private:
    void myRun(std::true_type) {
        if (F::f()) return;
        moreComputation();
    }

    void myRun(std::false_type) {
        F::f();
        moreComputation();
    }

    void moreComputation() { ... }
};
like image 112
AlwaysLearning Avatar answered Oct 10 '22 08:10

AlwaysLearning


You should use SFINAE instead of branching at runtime.

You function run should look like this:

template <typename F>
struct Algorithm {

    void run() {
        ... // some computation here

        doRun();
    }

    template<std::enable_if_t<std::is_same<decltype(F::f()), bool>::value, int> = 0>
    void doRun() {
        if (F::f()) {
            // do some more computations if needed or simply remove the if and return
        }
    }

    template<std::enable_if_t<!std::is_same<decltype(F::f()), bool>::value, int> = 0>
    void doRun() {
        F::f();

        ... // more computation, unless F::f() got rid of it
    }
};
like image 37
Guillaume Racicot Avatar answered Oct 10 '22 08:10

Guillaume Racicot