Template Magic to create variable length arguments to a function?

Discussion in 'C++' started by Clay_Culver@yahoo.com, Nov 11, 2005.

  1. Guest

    I have heard that it is possible to use classes + template magic to
    make standalone functions (or maybe just a functor which acts like a
    function) which can accept variable length arguments without using the
    elipse. For example, a funciton could take in 1 known type, then a
    variable list of other parameters:

    ReturnValueClass rvc = someFunction("Some known type", 1, 0.2,
    "string");
    ReturnValueClass rvc2 = someFunction("Some known type again", true, 0);

    This is without defining someFunction with the various different types
    involved. Boost.Python does this with their "call" method which always
    takes in a PyObject * as the first parameter, has a template for the
    return value, and everything else after the first parameter is
    magically accepted. This is example calling it:

    int return1 = call<int>(somePyObject1, 'a', 1, true);
    double return2 = call<double>(somePyObject2, 0.123, 4.2);
    bool return 3 = call<bool>(somePyObject3);

    Does anyone know where I can learn how to do this? I have started
    digging through the Boost.Python source to learn, but that is a
    veritable quagmire of templates, macros, and calls. It looks like it's
    done by making the call "function" a class/struct, and the function
    paramaters are made legal by templating+a macro expansion in the
    template. Beyond that I'm having trouble figure out how to do this (my
    use for this is very similar to what boost.python is doing, only with
    something other than Python).

    I'm not actually looking for someone to explain this complex topic here
    on the group (though that would be great). I really just want to know
    if anyone can link me to a minimal example/tutorial of how to do this
    that I can build on for my own application

    Thanks...
    , Nov 11, 2005
    #1
    1. Advertising

  2. Guest

    Note I cannot use the elipse for this because I need to preserve the
    type of the parameters. I'm planning on taking all of the parameters
    passing each one into an object's constructor that's super-overloaded
    to handle those types, and then putting each of those objects into an
    std::list/vector so that I can manipulate them.

    That's the idea anyway.
    , Nov 11, 2005
    #2
    1. Advertising

  3. it is not possible to implement variable argument lists with templates.

    the solution is to provide overloads for 1 to max arguments for your
    specific function/functor.

    e.g.

    template <typename R, typename T1>
    R x(T1 arg1);

    template <typename R, typename T1, typename T2>
    R x(T1 arg1, T2 arg2);

    template <typename R, typename T1, typename T2, ..., typename TN>
    R x(T1 arg1, T2 arg2, ..., TN argn);

    you can use boost::preprocessor to programatically expand your
    functions from a generic definition, this way you avoid a lot of
    redundant code and possibly bugs. the macros that you see in
    boost/python/call.hpp are an example of boost::preprocessor usage.

    -- peter
    peter steiner, Nov 11, 2005
    #3
  4. mlimber Guest

    wrote:
    > I have heard that it is possible to use classes + template magic to
    > make standalone functions (or maybe just a functor which acts like a
    > function) which can accept variable length arguments without using the
    > elipse. For example, a funciton could take in 1 known type, then a
    > variable list of other parameters:
    >
    > ReturnValueClass rvc = someFunction("Some known type", 1, 0.2,
    > "string");
    > ReturnValueClass rvc2 = someFunction("Some known type again", true, 0);
    >
    > This is without defining someFunction with the various different types
    > involved. Boost.Python does this with their "call" method which always
    > takes in a PyObject * as the first parameter, has a template for the
    > return value, and everything else after the first parameter is
    > magically accepted. This is example calling it:
    >
    > int return1 = call<int>(somePyObject1, 'a', 1, true);
    > double return2 = call<double>(somePyObject2, 0.123, 4.2);
    > bool return 3 = call<bool>(somePyObject3);
    >
    > Does anyone know where I can learn how to do this? I have started
    > digging through the Boost.Python source to learn, but that is a
    > veritable quagmire of templates, macros, and calls. It looks like it's
    > done by making the call "function" a class/struct, and the function
    > paramaters are made legal by templating+a macro expansion in the
    > template. Beyond that I'm having trouble figure out how to do this (my
    > use for this is very similar to what boost.python is doing, only with
    > something other than Python).
    >
    > I'm not actually looking for someone to explain this complex topic here
    > on the group (though that would be great). I really just want to know
    > if anyone can link me to a minimal example/tutorial of how to do this
    > that I can build on for my own application
    >
    > Thanks...


    There are several options. See this post by Cy for an example of one:

    http://groups.google.com/group/comp...90b30/5fbecab933500acc?hl=en#5fbecab933500acc

    Cheers! --M
    mlimber, Nov 11, 2005
    #4
  5. Guest

    Ahhh I didn't realize this was using a special preprocessor. Thanks.
    , Nov 11, 2005
    #5
  6. Guest

    Doh! Why didn't I think of that? That linked post seems to be the
    easiest solution, thanks.
    , Nov 11, 2005
    #6
  7. wrote:
    > I have heard that it is possible to use classes + template magic to
    > make standalone functions (or maybe just a functor which acts like a
    > function) which can accept variable length arguments without using the
    > elipse. For example, a funciton could take in 1 known type, then a
    > variable list of other parameters:
    >
    > ReturnValueClass rvc = someFunction("Some known type", 1, 0.2,
    > "string");
    > ReturnValueClass rvc2 = someFunction("Some known type again", true, 0);
    >
    > This is without defining someFunction with the various different types
    > involved. Boost.Python does this with their "call" method which always
    > takes in a PyObject * as the first parameter, has a template for the
    > return value, and everything else after the first parameter is
    > magically accepted. This is example calling it:
    >
    > int return1 = call<int>(somePyObject1, 'a', 1, true);
    > double return2 = call<double>(somePyObject2, 0.123, 4.2);
    > bool return 3 = call<bool>(somePyObject3);
    >
    > Does anyone know where I can learn how to do this? I have started
    > digging through the Boost.Python source to learn, but that is a
    > veritable quagmire of templates, macros, and calls. It looks like it's
    > done by making the call "function" a class/struct, and the function
    > paramaters are made legal by templating+a macro expansion in the
    > template. Beyond that I'm having trouble figure out how to do this (my
    > use for this is very similar to what boost.python is doing, only with
    > something other than Python).
    >
    > I'm not actually looking for someone to explain this complex topic here
    > on the group (though that would be great). I really just want to know
    > if anyone can link me to a minimal example/tutorial of how to do this
    > that I can build on for my own application
    >
    > Thanks...
    >

    Clay,

    C++ streams overload the >> and << operators enabling them to handle
    different types. This technique enables multiple inputs and by creating
    the << >> operators for your classes you create an easy mechanism for
    input/output. It also happens to have the bonus of being more type safe.
    In the example below the output of a string, integer, string, float and
    line end are handled in that order as one combined operation.

    int n = 6;
    float f = 3.26;

    std::cout << "integer = " << n << " , float = " << f << std::endl;

    I'm sure you should be able to find examples of how to implement
    the << >> operator overloads for classes.

    Hope this helps

    JFJB
    n2xssvv g02gfr12930, Nov 11, 2005
    #7
  8. wrote:
    > Ahhh I didn't realize this was using a special preprocessor. Thanks.


    well, boost::preprocessor is not a special preprocessor but rather a
    collection of magic preprocessor macros that allow you to do
    "programming" on macro level, enabling things like the repetition i
    showed above.

    you can use this library on all halfway modern compilers without
    external tools.

    see http://www.boost.org/libs/preprocessor/doc/index.html
    peter steiner, Nov 12, 2005
    #8
  9. peter steiner wrote:
    > wrote:
    >
    >>Ahhh I didn't realize this was using a special preprocessor. Thanks.

    >
    >
    > well, boost::preprocessor is not a special preprocessor but rather a
    > collection of magic preprocessor macros that allow you to do
    > "programming" on macro level, enabling things like the repetition i
    > showed above.
    >
    > you can use this library on all halfway modern compilers without
    > external tools.
    >
    > see http://www.boost.org/libs/preprocessor/doc/index.html
    >


    I'd stongly recommend reading this first

    http://boost-consulting.com/tmpbook/preprocessor.html

    Until I read this I really didn't get the boost preprocessor, now I
    think it's wonderful!

    john
    John Harrison, Nov 12, 2005
    #9
  10. Greg Guest

    peter steiner wrote:
    > it is not possible to implement variable argument lists with templates.
    >
    > the solution is to provide overloads for 1 to max arguments for your
    > specific function/functor.
    >
    > e.g.
    >
    > template <typename R, typename T1>
    > R x(T1 arg1);
    >
    > template <typename R, typename T1, typename T2>
    > R x(T1 arg1, T2 arg2);
    >
    > template <typename R, typename T1, typename T2, ..., typename TN>
    > R x(T1 arg1, T2 arg2, ..., TN argn);


    It is possible to use std::tr1::tuple to represent a variable argument
    list:

    #include <tr1/tuple>
    #include <iostream>

    using std::tr1::tuple;

    int main()
    {
    tuple<int, long, std::string> param_list(1, 2, "hello");

    DoSomething( param_list );
    ...
    }

    and then implement DoSomething:

    template <class Tuple>
    void DoSomething( Tuple params )
    {
    int numParams = tuple_size<Tuple>::value;

    std::cout << "number of args " << numParams << std::endl;

    if ( --numParams < 0 ) return;

    std::cout << "param 1" << get<0>(param_list);

    if ( --numParams < 0 ) return;

    std::cout << "param 2" << get<1>(param_list);

    if ( --numParams < 0 ) return;

    std::cout << "param 3" << get<2>(param_list);
    }

    Greg
    Greg, Nov 12, 2005
    #10
  11. Greg Guest

    peter steiner wrote:
    > it is not possible to implement variable argument lists with templates.
    >
    > the solution is to provide overloads for 1 to max arguments for your
    > specific function/functor.
    >
    > e.g.
    >
    > template <typename R, typename T1>
    > R x(T1 arg1);
    >
    > template <typename R, typename T1, typename T2>
    > R x(T1 arg1, T2 arg2);
    >
    > template <typename R, typename T1, typename T2, ..., typename TN>
    > R x(T1 arg1, T2 arg2, ..., TN argn);
    >
    > you can use boost::preprocessor to programatically expand your
    > functions from a generic definition, this way you avoid a lot of
    > redundant code and possibly bugs. the macros that you see in
    > boost/python/call.hpp are an example of boost::preprocessor usage.


    It is possible to use std::tr1::tuple to pass a variable argument list
    to a function:

    #include <tr1/tuple>
    #include <iostream>

    using std::tr1::tuple;
    using std::tr1::tuple_size;
    using std::tr1::get;

    // a function template accepting arbitrary parameter lists

    template <class Tuple>
    void DoSomething( Tuple params )
    {
    int numParams = tuple_size<Tuple>::value;

    std::cout << "number of args: " << numParams << std::endl;

    if ( --numParams < 0 ) return;

    std::cout << "param 1: " << get<0>(params) << std::endl;

    if ( --numParams < 0 ) return;

    std::cout << "param 2: " << get<1>(params) << std::endl;

    if ( --numParams < 0 ) return;

    std::cout << "param 3: " << get<2>(params) << std::endl;
    }

    int main()
    {
    // let's call DoSomething() and pass it
    // an int, a double and a std::string

    tuple<int, double, std::string> params(1, -3.14159, "hello");

    DoSomething( params );
    }

    Program Output:

    param 1: 1
    param 2: -3.14159
    param 3: hello

    Greg
    Greg, Nov 12, 2005
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    1
    Views:
    2,100
    Gianni Mariani
    Jun 8, 2007
  2. Replies:
    3
    Views:
    812
  3. Navaneeth
    Replies:
    4
    Views:
    550
    Kenny McCormack
    Nov 20, 2010
  4. Giles Bowkett
    Replies:
    9
    Views:
    399
    Giles Bowkett
    Dec 17, 2007
  5. oldyork90
    Replies:
    10
    Views:
    344
    Jorge
    Sep 27, 2008
Loading...

Share This Page