Generating One Class Member Per Variadic Template Argument

Generating one class member per variadic template argument

As you have already been hinted, the best way is to use a tuple:

template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2>
class MyClass {
std::tuple<std::vector<AcceptedTypes>...> vectors;
};

This is the only way to multiply the "fields" because you cannot magically make it spell up the field names. Another important thing may be to get some named access to them. I guess that what you're trying to achieve is to have multiple vectors with unique types, so you can have the following facility to "search" for the correct vector by its value type:

template <class T1, class T2>
struct SameType
{
static const bool value = false;
};

template<class T>
struct SameType<T, T>
{
static const bool value = true;
};

template <typename... Types>
class MyClass
{
public:
typedef std::tuple<vector<Types>...> vtype;
vtype vectors;

template<int N, typename T>
struct VectorOfType: SameType<T,
typename std::tuple_element<N, vtype>::type::value_type>
{ };

template <int N, class T, class Tuple,
bool Match = false> // this =false is only for clarity
struct MatchingField
{
static vector<T>& get(Tuple& tp)
{
// The "non-matching" version
return MatchingField<N+1, T, Tuple,
VectorOfType<N+1, T>::value>::get(tp);
}
};

template <int N, class T, class Tuple>
struct MatchingField<N, T, Tuple, true>
{
static vector<T>& get(Tuple& tp)
{
return std::get<N>(tp);
}
};

template <typename T>
vector<T>& access()
{
return MatchingField<0, T, vtype,
VectorOfType<0, T>::value>::get(vectors);
}
};

Here is the testcase so you can try it out:

int main( int argc, char** argv )
{
int twelf = 12.5;
typedef reference_wrapper<int> rint;

MyClass<float, rint> mc;
vector<rint>& i = mc.access<rint>();

i.push_back(twelf);

mc.access<float>().push_back(10.5);

cout << "Test:\n";
cout << "floats: " << mc.access<float>()[0] << endl;
cout << "ints: " << mc.access<rint>()[0] << endl;
//mc.access<double>();

return 0;
}

If you use any type that is not in the list of types you passed to specialize MyClass (see this commented-out access for double), you'll get a compile error, not too readable, but gcc at least points the correct place that has caused the problem and at least such an error message suggests the correct cause of the problem - here, for example, if you tried to do mc.access<double>():

 error: ‘value’ is not a member of ‘MyClass<float, int>::VectorOfType<2, double>’

Generate one method per type from variadic class template

Maybe someone else can do better, but I see only two ways

  1. Recursion inheritance

    You can define MyClass recursively as follows

    // recursive case
    template <typename T, typename ... Ts>
    struct MyClass : public MyClass<Ts...>
    {
    using MyClass<Ts...>::hello;

    virtual void hello (const T&) = 0;
    };

    // ground case
    template <typename T>
    struct MyClass<T>
    { virtual void hello (const T&) = 0; };

or


  1. variadic inheritance

    You can define another class/struct, say MyHello, that declare a
    single hello() method, and variadic inherit it from MyClass.

    template <typename T>
    struct MyHello
    { virtual void hello (const T&) = 0; };

    template <typename ... Ts>
    struct MyClass : public MyHello<Ts>...
    { };

The recursive example is compatible with type collision (that is: works also when a type is present more time in the list of template arguments MyClass; by example MyClass<int, double, int>).

The variadic inheritance case, unfortunately, isn't.

The following is a full compiling example

#if 1
// recursive case
template <typename T, typename ... Ts>
struct MyClass : public MyClass<Ts...>
{
using MyClass<Ts...>::hello;

virtual void hello (const T&) = 0;
};

// ground case
template <typename T>
struct MyClass<T>
{ virtual void hello (const T&) = 0; };
#else

template <typename T>
struct MyHello
{ virtual void hello (const T&) = 0; };

template <typename ... Ts>
struct MyClass : public MyHello<Ts>...
{ };

#endif

struct Derived : public MyClass<double, int>
{
inline void hello (const double&) override { }
inline void hello (const int&) override { }
};

int main()
{
Derived d;

d.hello(1.0);
d.hello(2);
}

-- EDIT --

The OP asks

how about a more complicated case where MyClass has more than one method and I always need to have one template argument (see edited question)?

From your question I don't understand what do you exactly want.

But supposing you want a pure virtual method, say goodmorning() that receive a MandT (the mandatory type), a pure virtual method hello() for every type following MandT or an hello() without arguments when the list after MandT is empty.

A possible solution is the following

// declaration and groundcase with only mandatory type (other cases
// intecepted by specializations)
template <typename MandT, typename ...>
struct MyClass
{
virtual void hello () = 0;

virtual ~MyClass () {}

virtual char * goodmorning (MandT const &) = 0;
};

// groundcase with a single optional type
template <typename MandT, typename OptT>
struct MyClass<MandT, OptT>
{
virtual void hello (OptT const &) = 0;

virtual ~MyClass () {}

virtual char * goodmorning (MandT const &) = 0;
};

// recursive case
template <typename MandT, typename OptT, typename ... MoreOptTs>
struct MyClass<MandT, OptT, MoreOptTs...>
: public MyClass<MandT, MoreOptTs...>
{
using MyClass<MandT, MoreOptTs...>::hello;

virtual void hello (OptT const &) = 0;

virtual ~MyClass () {}
};

Here the recursion is a little more complicated than before.

In case you instantiate a MyClass with only the mandatory type (by example: MyClass<char>) the main version ("groundcase with only mandatory type") is selected because the two specialization doesn't match (no first optional type).

In case you instantiate a Myclass with one optional type (say MyClass<char, double>) the specialization "groundcase with a single optional type" is selected because is the most specialized version.

In case you instantiate a MyClass with two or more optional type (say MyClass<char, double, int> start recursion (last specialization) until remain an single optional type (so the "groundcase with a single optional type" is selected).

Observe that I've placed the goodmorning() in both ground cases, because you don't need to define it recursively.

The following is a full compiling example

// declaration and groundcase with only mandatory type (other cases
// intecepted by specializations)
template <typename MandT, typename ...>
struct MyClass
{
virtual void hello () = 0;

virtual ~MyClass () {}

virtual char * goodmorning (MandT const &) = 0;
};

// groundcase with a single optional type
template <typename MandT, typename OptT>
struct MyClass<MandT, OptT>
{
virtual void hello (OptT const &) = 0;

virtual ~MyClass () {}

virtual char * goodmorning (MandT const &) = 0;
};

// recursive case
template <typename MandT, typename OptT, typename ... MoreOptTs>
struct MyClass<MandT, OptT, MoreOptTs...>
: public MyClass<MandT, MoreOptTs...>
{
using MyClass<MandT, MoreOptTs...>::hello;

virtual void hello (OptT const &) = 0;

virtual ~MyClass () {}
};

struct Derived0 : public MyClass<char>
{
void hello () override { }

char * goodmorning (char const &) override
{ return nullptr; }
};
struct Derived1 : public MyClass<char, double>
{
void hello (double const &) override { }

char * goodmorning (char const &) override
{ return nullptr; }
};

struct Derived2 : public MyClass<char, double, int>
{
void hello (double const &) override { }
void hello (int const &) override { }

char * goodmorning (char const &) override
{ return nullptr; }
};

int main()
{
Derived0 d0;
Derived1 d1;
Derived2 d2;

d0.hello();
d0.goodmorning('a');

d1.hello(1.2);
d1.goodmorning('b');

d2.hello(3.4);
d2.hello(5);
d2.goodmorning('c');
}

Variadic templates: One method per template argument

You can make a t_impl that holds the virtual function for a single T like

template <typename T>
class t_impl
{
public:
virtual void x(T &v) = 0;
};

and then t would inherit from it like

template <typename ...T>
class t : t_impl<T>... // can use public, protected or private inheritance
{
public:
using t_impl<T>::x...; // used to import t_impl<T>::x into the public space
// any additional common members
};

How to define member template function of variadic class template

You should sperate two sets of template parameters: one for the enclosing class template, and another one for the member function template itself. E.g.

template<class... Types> // for the enclosing class template
template<class T> // for the member template
T& Foo<Types...>::at(const std::string& key) {
...
}

template<class... Types> // for the enclosing class template
template<class T> // for the member template
void Foo<Types...>::insert(const std::string& key, const T& value) {
...
}

Variadic template class: Is it possible to implement one unique member function per variadic template argument?

You can't unpack the parameter pack across the body of the template definition as you were describing in the question, but you can use CRTP to assemble an class that inherits a hierarchy with templatized specializations for each of the type-parameters you supply:

#include <iostream>

template<class L, class... R> struct X;

template<class L>
struct X<L> { void handle(L& i) { std::cout << i.f() << "\n"; } };

template<class L, class... R>
struct X : public X<L>, public X<R...> { using X<L>::handle; using X<R...>::handle; };

struct A1 {
int f() { return 1; }
};

struct A2 {
int f() { return 2; }
};

struct B {
int f() { return 10; }
};

struct B1 : public B {
int f() { return 11; }
};

struct B2 : public B1 {
int f() { return 12; }
};

int main() {
X<A1, A2> x1;
A1 a1; A2 a2;
x1.handle(a1);
x1.handle(a2);

X<B, B1, B2> x2;
B b; B1 b1; B2 b2;
x2.handle(b);
x2.handle(b1);
x2.handle(b2);
}

How can I write a variadic template class?

Since there is no way to create a variadic data member, you'll have to use a std::tuple. The rest is pretty straightforward:

template<typename... Ts>
class tXmlBase
{
public:
tXmlBase(tXmlDoc* doc, Ts const&... id1);

// if you have no other virtual member, it's better to remove this.
virtual ~tXmlBase();
protected:
tXmlDoc* xml_doc;

pugi::xml_node root_node;
pugi::xml_node base_node;
pugi::xml_node parent_node;

std::tuple<Ts...> identifiers;

int n_childs;
int n_attr;
};

If you want to do something for all identifiers, you can use a variadic lambda and std::apply:

std::apply(
[](auto&... id) {
// expands into (id1.something(), id2.something(), ...)
(id.something(), ...);
},
identifiers
);

How can I wrap a variadic template member function of a variadic template class in SWIG?

I managed to get the wrapper generated and it works as expected.

This is a snippet of the old SWIG interface file:

...
template<typename ... Args>
void DoSomething(Args... args);
...

I replaced it with the following:

...
void DoSomething(int const, int const);
...

However I am still curious to find out whether there is a better way to do it so any further info (or pointers to reading materials/source code) would be appreciated.



Related Topics



Leave a reply



Submit