Topic: Reverse Variadic Templates


Author: Dennis Ferron <system.windows.codeslinger@gmail.com>
Date: Sun, 16 Aug 2009 01:45:41 CST
Raw View
The documented way of using variadic templates is to strip off the
first part of the parameter pack, and recursively call a shorter
version of yourself.  What if you did the opposite - recursively made
the pack larger?

Obviously that's just going to get bigger and bigger.  To make it do
something useful, we'll use an int Count and a partial specialization
on Count=0 to stop the expansion at some point before it blows up the
compiler!

Consider the following short program:

#include <iostream>

template<int Count, typename Arg0, typename... ArgN>
class reverse_variadic
{
// Notice the dummy int parameter that is added to make type list
larger:
     reverse_variadic<Count-1, int, Arg0, ArgN...> inner;
public:
     void hello()
     {
         std::cout << "Level: " << Count << " Num Args:" << sizeof...
(ArgN) << "\n";
         inner.hello();
     }
};

template<typename Arg0, typename... ArgN>
class reverse_variadic<0, Arg0, ArgN...>
{
public:
     void hello()
     {
         std::cout << "Hello from way down at the bottom with " <<
sizeof...(ArgN) << " args!\n";
     }
};

int main()
{
     reverse_variadic<10, int> test;
     test.hello();
     return 0;
}


When compiled and run with GCC 4.4.1 it will print out this:

Level: 10 Num Args:0
Level: 9 Num Args:1
Level: 8 Num Args:2
Level: 7 Num Args:3
Level: 6 Num Args:4
Level: 5 Num Args:5
Level: 4 Num Args:6
Level: 3 Num Args:7
Level: 2 Num Args:8
Level: 1 Num Args:9
Hello from way down at the bottom with 10 args!

Why would anyone want to do this?  Well what if you had the opposite
problem to the one solved by regular use of variadic templates?  What
if instead of needing your variadic template to _be_called_ with a
variable number of arguments (like the printf example that is often
given to show off variadic templates) you instead need _to_call_ a
function that might take arbitrarily many arguments?  (Which you would
do by unpacking a parameter pack at the bottom of the chain.)

My motivation is that I'm writing a script binding library that wraps
pointers-to-functions, and when the pointed-at function is called by
the script I have to marshal all the script objects into C++ values
and provide them as parameters to the function.  At the beginning,
rather than a pack of variadic function parameters, I actually have an
array of script objects that came in from the script VM and need to
somehow turn that into a variadic parameter pack.

Attempting to solve this particular function binding problem with
"normal" (reductive) use of variadic templates is problematic:
reductive variadic functions work by stripping off one argument, and
then recursively calling itself to do one more, but invoking a pointer-
to-function is an all-or-nothing proposition.  Think:  How would you
perform only 1/2 a function invocation?  You must pass all the
parameters when you make the function call!  You can't begin a call,
pass just one parameter the function needs, and say "uh, let me get
back to you on that."  If C++ supported currying then you really COULD
do this, but according to what I have read no one has successfully
implemented true, generic currying in C++.

I still don't know if reverse variadic templates will work for this.
My (vague, crude) strategy is that I'm want to try to use a reductive
variadic template to reduce the function-pointer's argument list to a
list of types (like Andrei Alexandrescu's Loki::TypeList construct)
while doing the conversion work on the script objects one by one on
the way down.  Then, at the bottom, I'm going to make it blossom back
up again with a reverse variadic template, adding one converted object
at a time to a growing parameter pack, to get back to a full parameter
pack which I can unpack and pass to the function at once.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: SG <s.gesemann@gmail.com>
Date: Sun, 16 Aug 2009 09:32:29 CST
Raw View
On 16 Aug., 09:45, Dennis Ferron
<system.windows.codeslin...@gmail.com> wrote:
> [...]
> My motivation is that I'm writing a script binding library that wraps
> pointers-to-functions, and when the pointed-at function is called by
> the script I have to marshal all the script objects into C++ values
> and provide them as parameters to the function.  At the beginning,
> rather than a pack of variadic function parameters, I actually have an
> array of script objects that came in from the script VM and need to
> somehow turn that into a variadic parameter pack.
> [...]
> Attempting to solve this particular function binding problem [...]
> If C++ supported currying then you really COULD
> do this, but according to what I have read no one has successfully
> implemented true, generic currying in C++.

Consider explaining in a little more detail, what problem you are
trying to solve. I think it's likely that this problem can be solved
already. You just don't see how.

Here's a simple sketch that uses type erasure of the function type at
the time of registering the functions. For the sake of simplicity I
ignored functions with a non-void return type:

    class incovable
    {
    public:
       virtual ~invocable() {}
       virtual status run(boost::any params[]) const = 0;
       virtual int arg_count() const = 0;
       virtual std::type_info const*const* arg_types() const = 0;
    };

    template<int... Idx> struct index_pack {};
    template<int N, int... Tail> struct make_index_pack
       : make_index_pack<N-1,N-1,Tail...> {};
    template<int... Tail> struct make_index_pack<0,Tail...> {
       typedef index_pack<Tail...> type; };

    template<typename... Params, int... Indices>
    inline void invoke_helper(
       void(*pf)(Params...),
       boost::any params[],
       index_pack<Indices...>)
    {
       pf(any_cast<Params>(params[Indices])...);
    }

    template<typename... Params>
    class void_func_impl
    {
       typedef typename make_index_pack<sizeof...(Params)>::type ipack;
       void (*pf)(Params...);
       std::type_info const* my_types[sizeof...(Params)];
    public:
       .....
       status run(boost::any params[]) const {
          .....
          invoke_helper(pf,params,ipack());
          .....
       }
       .....
    };

    template<typename... Params>
    void register_func(string name, void(*pf)(Params...)) {
       invocable* pi = new void_func_impl<Params...>(pf);
       // insert pi in some kind of map
    }

The parts I left blank (.....) should also be no problem to fill out.

Cheers!
SG


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Mathias Gaunard <loufoque@gmail.com>
Date: Mon, 17 Aug 2009 13:17:43 CST
Raw View
On 16 ao   t, 09:45, Dennis Ferron
<system.windows.codeslin...@gmail.com> wrote:
> The documented way of using variadic templates is to strip off the
> first part of the parameter pack, and recursively call a shorter
> version of yourself.  What if you did the opposite - recursively made
> the pack larger?
>
> Obviously that's just going to get bigger and bigger.  To make it do
> something useful, we'll use an int Count and a partial specialization
> on Count=0 to stop the expansion at some point before it blows up the
> compiler!

Macros do this perfectly, and that's actually how variadic behaviour
is emulated in libraries such as Boost.



--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]