Reputation: 12999
In pre-11 C++ I had something like this:
template<class T,class U,class V>
struct Foo : T,U,V {
bool init() {
if(!T::init() || !U::init() || !V::init())
return false;
// do local init and return true/false
}
};
I'd like to convert this to C++11 variadic syntax to get the benefit of the flexible length argument list. I understand the concept of unpacking the template arg list using recursion but I just can't seen to get the syntax right. Here's what I've tried:
template<typename... Features>
struct Foo : Features... {
template<typename F,typename... G>
bool recinit(F& arg,G&& ...args) {
if(!F::init())
return false;
return recinit<F,G...>(args...);
}
bool init() {
// how to call recinit() from here?
}
};
I would prefer the order of the calls to the base class init() functions to be left-to-right but it's not critical.
Upvotes: 8
Views: 2644
Reputation: 98338
This should work:
template<typename F, typename... T>
struct recinit;
template<typename F>
struct recinit<F> {
static bool tinit(F *) {
return true;
}
};
template<typename F, typename T, typename... G>
struct recinit<F, T, G...> {
static bool tinit(F *ptr) {
if (!ptr->T::init())
return false;
return recinit<F, G...>::tinit(ptr);
}
};
template<typename... Features>
struct Foo : Features... {
bool init() {
bool res = recinit<Foo, Features...>::tinit(this);
//use res wisely
}
};
Your problem is that you cannot write partial specializations of functions, only of classes/structs. And the auxiliary struct has to be outside of Foo
or else it will get the template arguments from the enclosing struct, and that would be bad.
You don't say but I'm assuming that init
is a non-static member function. If that is the case, the args
arguments make little sense: all of them should be this
! So just past this once and avoid the pack in the arguments. I tried passing this
as a void*
but that may be troublesome, so I just added an additional template argument to recinit
that will be Foo
.
And also, each time you do one recursive step remember to remove one parameter.
Upvotes: 4
Reputation: 126412
Maybe you could try something like this:
template<typename... Features>
struct Foo : Features...
{
bool init()
{
// Courtesy of Xeo :-)
auto il = {(static_cast<bool (Foo::*)()>(&Features::init))...};
return std::all_of(il.begin(), il.end(),
[this] (bool (Foo::*f)()) { return (this->*f)(); }
);
}
};
Here is an alternative, more verbose version that uses variadic templates:
template<typename... Features>
struct Foo : Features...
{
bool init()
{
return combine_and((&Features::init)...);
}
private:
bool combine_and()
{
return true;
}
template<typename F>
bool combine_and(F f)
{
return (this->*f)();
}
template<typename F1, typename... Fs>
bool combine_and(F1 f1, Fs... fs)
{
return ((this->*f1)() && combine_and(fs...));
}
};
Whichever solution you pick, this is how you could use it:
#include <iostream>
using namespace std;
struct A { bool init() { cout << "Hello " << endl; return true; } };
struct B { bool init() { cout << "Template " << endl; return true; } };
struct C { bool init() { cout << "World!" << endl; return true; } };
int main()
{
Foo<A, B, C> f;
bool res = f.init(); // Prints "Hello Template World!"
cout << res; // Prints 1
}
Upvotes: 3
Reputation: 545518
Your code has two problems. First, the rather mundane:
return recinit<F,G...>(args...);
You’ve already handled F
, leave it off from the list of arguments.
return recinit<G...>(args...);
(Additionally you should probably perfectly-forward the arguments.)
Secondly, the code won’t compile because your recursion has an anchor at runtime but not at compile time. That is, the compiler will try unpacking the argument pack G
ad infinitum. In order to prevent this you need to specialise the function for an empty template argument list.
Upvotes: 2