Is there a simple way? No. Can it be done using an unholy mess of garbled templates? Sure, why not.
Implementation
First, this is going to be a bit easier if we have a class rather than a function, simply because parameterized classes can be passed as template parameters. So I'm going to write a trivial wrapper around your myFunc
.
template <bool... Acc>
struct MyFuncWrapper {
template <typename T>
void operator()(T&& extra) const {
return myFunc<Acc...>(std::forward<T&&>(extra));
}
};
This is just a class for which MyFuncWrapper<...>()(extra)
is equivalent to myFunc<...>(extra)
.
Now let's make our dispatcher.
template <template <bool...> class Func, typename Args, bool... Acc>
struct Dispatcher {
auto dispatch(Args&& args) const {
return Func<Acc...>()(std::forward<Args&&>(args));
}
template <typename... Bools>
auto dispatch(Args&& args, bool head, Bools... tail) const {
return head ?
Dispatcher<Func, Args, Acc..., true >().dispatch(std::forward<Args&&>(args), tail...) :
Dispatcher<Func, Args, Acc..., false>().dispatch(std::forward<Args&&>(args), tail...);
}
};
Whew, there's quite a bit to explain there. The Dispatcher
class has two template arguments and then a variadic list. The first two arguments are simple: the function we want to call (as a class) and the "extra" argument type. The variadic argument will start out empty, and we'll use it as an accumulator during the recursion (similar to an accumulator when you're doing tail call optimization) to accumulate the template Boolean list.
dispatch
is just a recursive template function. The base case is when we don't have any arguments left, so we just call the function with the arguments we've accumulated so far. The recursive case involves a conditional, where we accumulate a true
if the Boolean is true
and a false
if it's false
.
We can call this with
Dispatcher<MyFuncWrapper, TypeOfExtraArgument>()
.dispatch(extraArgument, true, true, false);
However, this is a bit verbose, so we can write a macro to make it a bit more approachable.1
#define DISPATCH(F, A, ...) Dispatcher<F, decltype(A)>().dispatch(A, __VA_ARGS__);
Now our call is
DISPATCH(MyFuncWrapper, extraArgument, true, true, false);
Complete Runnable Example
Includes a sample myFunc
implementation.
#include <utility>
#include <iostream>
#define DISPATCH(F, A, ...) Dispatcher<F, decltype(A)>().dispatch(A, __VA_ARGS__);
template <bool a, bool b, bool c, typename T>
void myFunc(T&& extra) {
std::cout << a << " " << b << " " << c << " " << extra << std::endl;
}
template <bool... Acc>
struct MyFuncWrapper {
template <typename T>
void operator()(T&& extra) const {
return myFunc<Acc...>(std::forward<T&&>(extra));
}
};
template <template <bool...> class Func, typename Args, bool... Acc>
struct Dispatcher {
auto dispatch(Args&& args) const {
return Func<Acc...>()(std::forward<Args&&>(args));
}
template <typename... Bools>
auto dispatch(Args&& args, bool head, Bools... tail) const {
return head ?
Dispatcher<Func, Args, Acc..., true >().dispatch(std::forward<Args&&>(args), tail...) :
Dispatcher<Func, Args, Acc..., false>().dispatch(std::forward<Args&&>(args), tail...);
}
};
int main() {
DISPATCH(MyFuncWrapper, 17, true, true, false);
DISPATCH(MyFuncWrapper, 22, true, false, true);
DISPATCH(MyFuncWrapper, -9, false, false, false);
}
Closing Notes
The implementation provided above will let myFunc
return values as well, although your example only included a return type of void
, so I'm not sure you'll need this. As written, the implementation requires C++14 for auto
return types. If you want to do this under C++11, you can either change all the return types to void
(can't return anything from myFunc
anymore) or you can try to hack together the return types with decltype
. If you want to do this in C++98, ... ... ... ... good luck
1 This macro is susceptible to the comma problem and thus won't work if you pass it zero Booleans. But if you're not going to pass any Booleans, you probably shouldn't be going through this process anyway.