Question about using variadic templates with template patterns

Jun 22, 2016 at 10:36am
In trying to solve my latest programming dilemma I learned about variadic templates. However, I have been unable to find how to use/access the argument list when dealing with the list of typenames/classes in the way that I want to use them.

Lets say I have a couple of template patterns (mixture of curiously recurring template patterns/strategy patterns/policy design templates). Now I need to have an inheritable class that provides an initializer, an overloader or both.

Here is a list of example template pattern classes:

1
2
3
class TemplatePatternPointer {};
template<typename ClassName> TemplatePatternClassA : TemplatePatternPointer {};
template<typename ClassName> TemplatePatternClassB : TemplatePatternPointer {};



Here is a list types to be used in the example

1
2
3
4
class ClassAA : TemplatePatternClassA<ClassAA> {};
class ClassBA : TemplatePatternClassA<ClassBA> {};
class ClassAB : TemplatePatternClassB<ClassAB> {};
class ClassBB : TemplatePatternClassB<ClassBB> {};



Here is the example of how I want to use the variadic template class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "SomeListClass.h"

template<class A, class...> class VariadicPatternProviderInterface // requires at least one parameter
{
	SomeListClass<TemplatePatternPointer*> patternInstances;

	// for each item in the template parameter pack provide an overload
	// and yes... I know I should check and make sure that the pointer is of
	// the correct type
	createNewClassInstance(A* pointer) 
	{ 
		TemplatePatternPointer* pointer = new A();
		patternInstances.append(pointer); 
	}
	createNewClassInstance(...* pointer) 
	{ 
		TemplatePatternPointer* pointer = new ...(); 
		patternInstances.append(pointer);
	}

	// and/or for each item in the template parameter pack
	initPatterns()
	{
		// either call the above functions or...

		patternInstances.append(new A);
		patternInstances.append(new ...);
	}
};

// now for the point usinng all the code examples above
class MyProviderClass : VariadicPatternProviderInterface<ClassAA, ClassAB, ClassBA, ClassBB> {...};


Here are my questions:
1. Is doing this sort of thing with variadic templates possible?
2. What syntax do I use for cycling through the template parameter pack?
Last edited on Jun 22, 2016 at 10:58am
Jun 22, 2016 at 12:00pm
I'd say it is... though, not in the way you want.

Rather than defining a bunch of overloads based on the parameter pack, my solution basically involves just doing createNewClassInstance as a template function (i.e., a function with infinite overloads), and using std::enable_if to 'remove' a function if called with a type that isn't in the argument pack:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "SomeListClass.h"

#include <type_traits>

// helper to check all args eval to true
constexpr bool is_all_true() { return true; }

template <typename ...Args>
constexpr bool is_all_true(bool first, Args... rest) {
    return first && is_all_true(rest...);
}

// class template
template <typename ...Args>
class VariadicPatternProviderInterface {
    static_assert(sizeof...(Args) > 0, "Must provide at least one parameter");

public:
    // Add a new class instance. If you need to, you can change the 
    // std::is_same to std::is_base_of or something, too.
    // If you want better error messages, consider if replacing the std::enable_if
    // with a static_assert in the function body would be appropriate for you.
    template <typename T>
    std::enable_if<is_all_true(std::is_same<T, Args>...)>::type
    createNewClassInstance(T* pointer) {
        TemplatePatternPointer* pointer = new T(); // ? This shadows function param
        patternInstances.append(pointer);
    }

    void initPatterns() {
        // some hackery to expand a list of void-returning function calls
        using expand_type = int[];
        (void) expand_type { (patternInstances.append(new Args), 0)... };
    }

protected:
    SomeListClass<TemplatePatternPointer*> patternInstances;
};


I'm sure there's a simpler and 'prettier' way of doing it this way, but that's what I thought of. Also note that that's completely untested code, and may not even work at all. You should get the general idea, though.

2. What syntax do I use for cycling through the template parameter pack?

To cycle through a template parameter pack, the way to do is a recursive template declaration. Often it can be hard to work out how to apply it in a particular circumstance, though. My code above has an example of doing something like it with my is_all_true function.
Last edited on Jun 22, 2016 at 12:04pm
Jun 22, 2016 at 12:40pm
Thanks a bunch! That will probably work. I will take other suggestions from anyone else just in case in the meantime.
Jun 22, 2016 at 1:30pm
I don't suppose there is a way to do this without using constexpr? My IDE doesn't support that keyword. I will search for this myself but in the meantime I thought I might save time by asking (just in case).
Jun 22, 2016 at 1:42pm
Well, you could create a template class to do something similar, probably. And I see I completely stuffed up the logic for that function, too.

Maybe something like this?
1
2
3
4
5
6
7
8
9
10
11
template <bool b, typename ...Args>
struct is_any_true_impl : is_any_true_impl<Args...> {};

template <typename ...Args>
struct is_any_true_impl<true, Args...> { static const value = true; };

template <>
struct is_any_true_impl<false> { static const value = false; };

template <typename ...Args>
struct is_any_true : is_any_true_impl<false, Args...> {};

Used like:
1
2
3
4
5
template <typename T>
std::enable_if<is_any_true<std::is_same<T, Args>::value...>::value>::type
createNewClassInstance(T* pointer) {
    // ...
}

Again, not tested, could well not work, but you should get the idea.

EDIT:
Seems not. I both forgot the using expressions and that you'll need to work out some way of casting the generic types to bools.

EDIT 2:
I'm really stupid. This is why you don't think really late at night :)
1
2
3
4
5
template <bool b, bool... rest>
struct is_any_true { static const bool value = b || is_any_true<rest...>::value; };

template <bool b>
struct is_any_true<b> { static const bool value = b; };
Last edited on Jun 22, 2016 at 1:57pm
Jun 22, 2016 at 2:28pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <typeinfo>
#include <type_traits>
#include <cassert>

struct pattern { virtual ~pattern() = default ; /* ... */ };
template < typename T > struct pattern_a : pattern { /* ... */ };
template < typename T > struct pattern_b : pattern { /* ... */ };

struct pattern_A1 : pattern_a<pattern_A1> { /* ... */ };
struct pattern_A2 : pattern_a<pattern_A2> { /* ... */ };

struct pattern_B1 : pattern_b<pattern_B1> { /* ... */ };
struct pattern_B2 : pattern_a<pattern_B2> { /* ... */ };

template < typename... > struct pattern_provider ; // not defined (requires at least one parameter)
template < typename T > struct pattern_provider<T>
{
    template < std::size_t > pattern& get() { return pat ; }
    T pat ;
};

template < typename FIRST, typename... REST >
struct pattern_provider<FIRST,REST...>
{
    template < std::size_t INDEX > pattern& get() { return INDEX == 0 ? pat : tail.template get<INDEX-1>() ; }
    FIRST pat ;
    pattern_provider<REST...> tail ;
    // ...
};

struct my_pattern_provider : pattern_provider< pattern_A1, pattern_A2, pattern_B2 > { /* ... */ };

int main()
{
    my_pattern_provider provider ;

    assert( typeid( provider.get<0>() ) == typeid(pattern_A1) ) ;
    std::cout << typeid( provider.get<0>() ).name() << '\n' ;

    assert( typeid( provider.get<1>() ) == typeid(pattern_A2) ) ;
    std::cout << typeid( provider.get<1>() ).name() << '\n' ;

    assert( typeid( provider.get<2>() ) == typeid(pattern_B2) ) ;
    std::cout << typeid( provider.get<2>() ).name() << '\n' ;

    assert( typeid( provider.get<22>() ) == typeid(pattern_B2) ) ; // invalid index; silently returns the last pattern
}

http://coliru.stacked-crooked.com/a/79a1125845062642
http://rextester.com/AYZ35846
Topic archived. No new replies allowed.