Question

Is it possible to parameterize non-type template arguments? I'm trying to generate a thunk that forwards its arguments to one of two compile time constant functions based on some runtime check, to get something hopefully along the lines of this:

#include <stdlib.h>

int a(int, int, char) {
    return 0;
}

int b(int, int, char) {
    return 0;
}

// This doesn't work
template<typename ReturnType, typename... Params>
template<ReturnType (*first)(Params...), ReturnType (*second)(Params...)>
ReturnType coin_flip(Params... params) {
    if (rand() % 2) {
        return first(params...);
    } else {
        return second(params...);
    }
}

int main() {
    return coin_flip<a, b>(1, 2, '3');
}
Was it helpful?

Solution

There is a workaround which uses type template parameters (via std::integral_constant) and macros:

#include <type_traits>

template <typename, typename>
struct coin_flip;

template
    <
        typename ReturnType,
        typename... Params,
        ReturnType (*first)(Params...),
        ReturnType (*second)(Params...)
    >
struct coin_flip
    <
        std::integral_constant<ReturnType (*)(Params...), first>,
        std::integral_constant<ReturnType (*)(Params...), second>
    >
{
    static
    ReturnType call(Params&&... params) {
        if (rand() % 2) {
            return first(std::forward<Params>(params)...);
        } else {
            return second(std::forward<Params>(params)...);
        }
    }
};

#define FUNCTION_CONSTANT(f) std::integral_constant<decltype(&f), f>

#define COIN_FLIP(first, second, ...) \
    coin_flip<FUNCTION_CONSTANT(first), FUNCTION_CONSTANT(second)>::call(__VA_ARGS__)

Example of using:

std::cout << COIN_FLIP(a, b, 1, 2, '3') << std::endl;

OTHER TIPS

Following may help:

template<typename F, F f1, F f2, typename... Params>
auto coin_flip(Params&&... params) -> decltype(f1(std::forward<Params>(params)...)) {
    if (rand() % 2) {
        return f1(std::forward<Params>(params)...);
    } else {
        return f2(std::forward<Params>(params)...);
    }
}

int main() {
    return coin_flip<decltype(&a), &a, &b>(1, 2, '3');
}

You might pass the functions as arguments to coin_flip (not as template parameters):

#include <iostream>
#include <cstdlib>
#include <type_traits>

int a(int, int, char) {
    std::cout << 'a';
    return 0;
}

int b(int, int, char) {
    std::cout << 'b';
    return 0;
}

template<typename FnA, typename FnB, typename... Params>
typename std::result_of<FnA&(Params...)>::type
coin_flip(FnA a, FnB b, Params... params) {
    // For testing, the condition is removed here
    a(params...);
    return b(params...);
}

int main() {
    coin_flip(a, b, 1, 2, '3');
    std::cout << '\n';
    return  0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top