Variadic functions with multiple parameter packs, problem with brace-enclosed initializer list

I have this mechanics in my project. I need to call loop with any number of shared_ptr's of Behaviors and Listeners but can't seem to get it to work. If I input a list of Behaviors into loop(), it works. If I put in a list of Listeners, I need to comment out line 28 to make it to work. I need line 45 to work.

Thanks
Chris

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
49
50
#include <cstdlib>
#include <memory>
#include <set>

using namespace std;

class Robot {
public:
    Robot(){};
};

class Behavior {
public:
Behavior(Robot &o): r(o){};
    
    Robot &r;
};

class Listener{
public:
    Listener(){};
};

class MacroBehavior : public Behavior {
public:
    MacroBehavior(Robot &o) : Behavior(o) {};
    template <typename ...ArgsL, typename ...ArgsB> void loop( ArgsL... l_args,  ArgsB... b_args ){
        std::set<std::shared_ptr<Behavior> >  behaviors {b_args...}; //comment out to get x->loop(l,m);  working
        std::set<std::shared_ptr<Listener> >  listeners {l_args...};
    }
};



int main(int argc, char** argv) {
    Robot create;
    std::shared_ptr<MacroBehavior> x= std::make_shared<MacroBehavior>(create);
    std::shared_ptr<MacroBehavior> y= std::make_shared<MacroBehavior>(create);
    std::shared_ptr<MacroBehavior> z= std::make_shared<MacroBehavior>(create);
    std::shared_ptr<Listener> l= std::make_shared<Listener>();
    std::shared_ptr<Listener> m= std::make_shared<Listener>();
    x->loop(y,z);  //input two MacroBehaviors OK
    //x->loop(l,m);    //only works if line 28 is commented out
    
    //x->loop(y,z,l,m); //This is what I need to get working

    return 0;
}

Last edited on
This is a clue I got from running the code in the online C++ shell. I did not get this error from GCC.

 
In instantiation of 'void MacroBehavior::loop(ArgsL ..., ArgsB ...) [with ArgsL = {}; ArgsB = {std::shared_ptr<MacroBehavior>, std::shared_ptr<MacroBehavior>, std::shared_ptr<Listener>, std::shared_ptr<Listener>}]':


Apparently, all the parameters are assigned to ArgsB with ArgsL empty.

Chris
I found a solution, but there's got to be something simpler whilst for readability preserving the function invocation as :

 
x->loop(y,z,l,m);


Here is the solution I came up with.

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <cstdlib>
#include <memory>
#include <set>
#include <iostream>

using namespace std;

class Basic{
public:
    Basic(){};
    virtual std::string get_id()=0;
};

class Robot {
public:
    Robot(){};
};

class Behavior : public Basic {
public:
Behavior(Robot &o): r(o){};
virtual std::string get_id() { return id ; };
    Robot &r;
    std::string id="Behavior";
};

class Listener :public Basic{
public:
    Listener(){};
    virtual std::string get_id() { return id ; };
    std::string id="Listener";
};

class MacroBehavior : public Behavior {
public:
    MacroBehavior(Robot &o) : Behavior(o) {};
    template <typename ...ArgsB> void loop(ArgsB... b_args ){

        std::set<std::shared_ptr<Basic> >  arguments {b_args...};
        
        std::set<std::shared_ptr<Behavior> > behaviors;
        std::set<std::shared_ptr<Listener> > listeners;
        for (auto &b : arguments) {
            if (b->get_id() == "Listener")
                listeners.insert(std::dynamic_pointer_cast<Listener>(b));
            else
                behaviors.insert(std::dynamic_pointer_cast<Behavior>(b));
        }
        
        for (auto &b : behaviors) {
            std::cout << "Behavior : I am a " << b->get_id() << std::endl;
        }
        
        for (auto &l : listeners) {
            std::cout << "Listener : I am a " << l->get_id() << std::endl;
        }
    }
};



int main(int argc, char** argv) {
    Robot create;
    std::shared_ptr<MacroBehavior> x= std::make_shared<MacroBehavior>(create);
    std::shared_ptr<MacroBehavior> y= std::make_shared<MacroBehavior>(create);
    std::shared_ptr<MacroBehavior> z= std::make_shared<MacroBehavior>(create);
    std::shared_ptr<Listener> l= std::make_shared<Listener>();
    std::shared_ptr<Listener> m= std::make_shared<Listener>();
   
    
    x->loop(y,z,l,m); //This is what I need to get working, but without resorting to the convoluted solution above. 

    return 0;
}



Thanks,
Chris
Last edited on
Use std::initializer_list, and then x->loop( {y,z}, {l,m} ); perhaps?

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 <memory>
#include <set>
#include <iostream>
#include <initializer_list>

class Robot {};

struct Behavior { Behavior(Robot &o): r(o) {} Robot &r; };

class Listener{};

struct MacroBehavior : public Behavior {

    MacroBehavior(Robot &o) : Behavior(o) {};

    void loop( std::initializer_list< std::shared_ptr<Behavior> > b_args,
               std::initializer_list< std::shared_ptr<Listener> > l_args ) {

        std::set< std::shared_ptr<Behavior> >  behaviors { b_args.begin(), b_args.end() };
        std::set< std::shared_ptr<Listener> >  listeners { l_args.begin(), l_args.end() };
    }
};

int main() {

    Robot create;

    std::shared_ptr<MacroBehavior> x = std::make_shared<MacroBehavior>(create);

    x->loop( { std::make_shared<MacroBehavior>(create), std::make_shared<MacroBehavior>(create) },
             { std::make_shared<Listener>(), std::make_shared<Listener>() } ) ;
}

http://coliru.stacked-crooked.com/a/618c0bc129aae4d8
Thanks, I am using your solution. I used std::initializer_list in one of my class member functions before but I didn't like the extra curly brackets inside the () operator especially since all the parameters are of one type. I made the curly brackets go away with the use of Variadics. In this case though, using the curly brackets logically groups the different types of parameters. I am curious though, is there no way to use multiple parameter packs in a variadic function?

Thanks
Chris

> I am curious though, is there no way to use multiple parameter packs in a variadic function?

Multiple parameter packs are allowed only if they can be deduced unambiguously.

... A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template ...
[Example:
1
2
3
4
    // ...
    // U can be neither deduced from the parameter-type-list nor specified
    template<class... T, class... U> void f() {} // error
    template<class... T, class U> void g() {} // error 

—end example ] - IS


Two parameter packs, both at the top level:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

template < typename... T, typename... U > void foo( T&&... , U&&... )
{
    std::cout << "sizeof...(T): " << sizeof...(T) << "  sizeof...(U): " << sizeof...(U) << '\n' ;
}

int main()
{
    foo( 1, 'a', "hello", 7.85, std::cout, 7, 'x' ) ; // *** error: no matching function - candidate 'foo' is not viable (clang++, microsoft)
                                                      // g++ swallows this, with sizeof...(T): 0  sizeof...(U): 7
}

http://coliru.stacked-crooked.com/a/a34d4dd84e6d405e
http://rextester.com/CBJU73760


Two parameter packs, only the second one is at the top level:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <tuple>

template < typename... T, typename... U > void foo( std::tuple<T...>, U&&... )
{
    std::cout << "sizeof...(T): " << sizeof...(T) << "  sizeof...(U): " << sizeof...(U) << '\n' ;
}

int main()
{
    foo( std::make_tuple( 1, 'a' ), "hello", 7.85, std::cout, 7, 'x' ) ; // fine: sizeof...(T): 2  sizeof...(U): 5
}

http://coliru.stacked-crooked.com/a/0a707617f375298d
http://rextester.com/PFEKT23006
So basically, you can combine multiple parameter packs if one of the packs is wrapped in an object, a tuple in this case. And it seems, you can put in any number of objects in front of a parameter pack if those objects are declared in the function argument before the parameter pack. However, if they are put in after the parameter pack declaration, the parameter pack swallows the trailing objects and so the function signature cannot be matched.

Thanks for your time and interest
Chris
Topic archived. No new replies allowed.