Parameter Pack Confusion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <type_traits>

template<typename T, typename...> struct require { using type = T; };
template<typename... Args> void ignore(Args const &...){}

template<typename... Ints>
auto f(Ints... x) -> typename require<void, typename std::enable_if<std::is_convertible<Ints, int>::value>::type...>::type
{
	ignore((std::cout << x << std::endl)...);
}

int main()
{
	f(1);
	f(2, 3);
}
1
3
2
http://ideone.com/de4Scz

1. Why do I need the ignore function?
2. Why is the order reversed for the second function call? Undefined behavior, evaluating expressions in function parameters. So, how can I make it defined and in order?
3. Why can't I just write void f(int... x){ /**/ } and have it automagically make it a constrained template?
Last edited on
> Why do I need the ignore function?

If the intent is to merely print out the values, you do not.


> So, how can I make it defined and in order?

By unpacking the parameters in order.

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

template< typename T > std::ostream& foo_( T&& v )
{ return std::cout << v << ' ' ; }

template< typename FIRST, typename... REST >
std::ostream& foo_( FIRST&& first, REST&&... rest )
{
    foo_(first) ;
    return foo_(rest...) ;
}

template< typename... T > std::ostream& foo( T&&... args )
{ return foo_(args...) << '\n' ; }

int main()
{
	foo(1);
	foo(2, 3);
	std::complex<double> c { 4.5, 6.7 } ;
	foo( "hello", "world", 8, 9.012, c, std::addressof(c) ) ;
}

http://coliru.stacked-crooked.com/a/e9ad6b862056809e


> Why can't I just write void f(int... x){ /**/ } and have it automagically make it a constrained template?

We don't need variadic templates to handle a (run-time) sequence of integers, do we?
We already have:
1
2
3
4
5
6
7
void f( std::initializer_list<int> il ) {}

template < typename ITERATOR >
void f( ITERATOR begin, ITERATOR end ) ;

template < typename SEQUENCE >
void f( const SEQUENCE& seq ) ;

And if required, we can use SFINAE to restrict the value type to int.
LB wrote:
So, how can I make it defined and in order?

One trick I see people use is to rely on the well-defined-ness of the order of list-initialization:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <type_traits>

template<typename T, typename...> struct require { using type = T; };
template<typename... Args> void ignore(Args const &...){}

template<typename... Ints>
auto f(Ints... x) -> typename require<void, typename std::enable_if<std::is_convertible<Ints, int>::value>::type...>::type
{
    using swallow = int[];
    (void)swallow{0, (void( // generic approach, can be simplified here a bit
                            std::cout << x << std::endl
                          ), 0)...};
}

int main()
{
    f(1);
    f(2, 3);
}
Last edited on
Thanks guys :)
OK, now I'm trying to apply this into my project and I'm finding out that I asked my question the wrong way.

Let's say I have two functions, GetFirstParam() and GetNextParam(), which need to be called to get the parameters passed to a function:
1
2
3
4
5
6
7
8
9
10
11
12
13
int &PrivateFunc()
{
    static int p;
    return p;
}
int GetFirstParam()
{
    return PrivateFunc() = 0;
}
int GetNextParam()
{
    return ++PrivateFunc();
}
1
2
3
int f();
int g(int);
int h(int, int, int);
1
2
3
4
5
template<typename... IntArgs>
int CallFunc(int (&f)(IntArgs...))
{
    //?
}
How can I implement CallFunc?

EDIT: I think I know, I just need to use an integer template parameter to track which parameter index I am on, but that's as far as I have got...
Last edited on
The result of GetFirstParam() and GetNextParam() are known only at run- time, so a pure compile-time technique would not work.

If the number of parameters are not too large, a brute force approach should suffice:

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
#include <iostream>

int& func()
{
    static int p;
    return p;
}

int first_param() { return func() = 0 ; }

int next_param() { return ++func() ; }

template < std::size_t N > struct do_call_fn ;

template <> struct do_call_fn<0>
{ template < typename FN > static int call( FN fn) { return fn() ; } };

template <> struct do_call_fn<1>
{
    template < typename FN > static int call( FN fn)
    { return fn( first_param() ) ; }
};

template <> struct do_call_fn<2>
{
    template < typename FN > static int call( FN fn)
    { auto a = first_param() ; return fn( a, next_param() ) ; }
};

template <> struct do_call_fn<3>
{
    template < typename FN > static int call( FN fn)
    {
        auto a = first_param() ;
        auto b = next_param() ;
        return fn( a, b, next_param() ) ;
    }
};

template <> struct do_call_fn<4>
{
    template < typename FN > static int call( FN fn)
    {
        auto a = first_param() ;
        auto b = next_param() ;
        auto c = next_param() ;
        return fn( a, b, c, next_param() ) ;
    }
};
template < typename... ARGS > int call_fn( int (&fn)( ARGS... ) )
{ return do_call_fn< sizeof...(ARGS) >::call(fn) ; }

int f() { std::cout << "f()\n" ; return 0 ; }
int g(int) { std::cout << "g(int)\n" ; return 1 ; }
int h(int,int) { std::cout << "h(int,int)\n" ; return 2 ; }
int i(int,int,int) { std::cout << "i(int,int,int)\n" ; return 3 ; }
int j(int,int,int,int) { std::cout << "j(int,int,int,int)\n" ; return 3 ; }

int main()
{
   call_fn(f) ;
   call_fn(g) ;
   call_fn(h) ;
   call_fn(i) ;
   call_fn(j) ;
}

http://ideone.com/qOKoAv

If the number of parameters could be large, recursively binding parameters one by one with std::bind() would be a more elegant approach.

An alternative to consider is to write each of these functions to take a tuple<> (of the appropriate arity) as the only parameter.
Thanks, I think I will have to go with recursively building parameters with std::bind (or doing some magic with tuple_cat). I can't have the functions accept tuples, unfortunately.
If the the evaluation of the parameters has referential transparency - ie. they have no side effects and their evaluation can be indeterminately sequenced - things are a lot easier because a strictly sequenced left to right evaluation is not mandatory; we can evaluate them from right to left. For instance:

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
#include <iostream>

int param_at( std::size_t pos ) { return pos+3 ; }

template < std::size_t N > struct do_call_fn ;

template <> struct do_call_fn<0>
{
    template < typename FN, typename... ARGS >
    static int call( FN fn, ARGS... args ) { return fn(args...) ; }
};

template <> struct do_call_fn<1>
{
    template < typename FN, typename... ARGS >
    static int call( FN fn, ARGS... args )
    { return fn( param_at(1), args... ) ; }
};

template < std::size_t N > struct do_call_fn
{
    template < typename FN, typename... ARGS >
    static int call( FN fn, ARGS... args )
    { return do_call_fn<N-1>::call( fn, param_at(N), args... ) ; }
};

template < typename... ARGS > int call_fn( int (&fn)( ARGS... ) )
{ return do_call_fn< sizeof...(ARGS) >::call(fn) ; }

int f() { std::cout << "f()\n" ; return 0 ; }
int g(int) { std::cout << "g(int)\n" ; return 1 ; }
int h(int,int) { std::cout << "h(int,int)\n" ; return 2 ; }
int i(int,int,int) { std::cout << "i(int,int,int)\n" ; return 3 ; }
int j(int,int,int,int) { std::cout << "j(int,int,int,int)\n" ; return 4 ; }
int k(int,int,int,int,int,int,int,int)
{ std::cout << "k(int,int,int,int,int,int,int,int)\n" ; return 8 ; }

int main()
{
   call_fn(f) ;
   call_fn(g) ;
   call_fn(h) ;
   call_fn(i) ;
   call_fn(j) ;
   call_fn(k) ;
}

http://coliru.stacked-crooked.com/a/fa1e59faff9ac4a3
The order of evaluation is important, and needs to be left to right. I managed a solution with tuple_cat, thought I've not tested it yet.

By the way, which has more runtime overhead - recursive std::bind or recursive tuple_cat?
Last edited on
> By the way, which has more runtime overhead - recursive std::bind or recursive tuple_cat?

Recursive std::bind would be more expensive.

Have you considered using boost::fusion::invoke()?
http://www.boost.org/doc/libs/1_55_0/libs/fusion/doc/html/fusion/functional/invocation/functions/invoke.html
Last edited on
Ah, so I guessed right. Thanks for all your help :)
Topic archived. No new replies allowed.