Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any metaprogramming way to generate overloads for various numbers of template parameters?

I'm trying to create a set of function templates that can take different types and numbers of parameters, like this:

template <T0>
void call(T0 arg0);

template <T0, T1>
void call(T0 arg0, T1 arg1);

template <T0, T1, T2>
void call(T0 arg0, T1 arg1, T2 arg2);

template <T0, T1, T2, T3>
void call(T0 arg0, T1 arg1, T2 arg2, T3 arg3);

template <T0, T1, T2, T3, T4>
void call(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4);

[...]

The parameters are all treated the same within the functions (as arguments to a single-parameter template function). This is for a library, so extra effort on my part is acceptable, if it means less effort or a more pleasing interface for the library user.

I've had to do this several times for different projects, and I'm heartily tired of having to manually write all of them by hand. It gets worse when I don't know beforehand the maximum number of parameters the project using the library will need.

Before I start writing a Python script to generate all of the overloads, is there some metaprogramming way to have the compiler do it for me instead?

like image 515
Head Geek Avatar asked Mar 29 '11 15:03

Head Geek


4 Answers

There is no standard way of doing this, but it can be done in C++0x, the upcoming (but not-yet-official) standard using a technique called 'variadic templates.' See here for an example by yours truly. Quoting myself:

#include <iostream>

template<typename Format>
void meheer(const Format& format) {
  std::cout << format << std::endl;;
}

template<typename Format, typename Elt, typename ... Args>
void meheer(const Format& format, const Elt & e, const Args&... args) {
  std::cout << format << e;
  meheer(format, args...);
}

template<typename Format, typename ... Args>
void ohai(const Format& format, const Args&... args) {
  meheer(format, args...);
}

int main(int argc, char ** argv) {
  ohai(1,2,3);

  return EXIT_SUCCESS;
}

Output: (compiled with GCC, with flag -std=c++0x)

12131

Conceptually, this is similar to a basic recursive match using pattern matching (as in a functional language), but unrolled at compile time.

If you do not want to include a not-yet-standard feature, then the 'many similar definitions using python script' approach is not a bad way to go. The boost libraries use a similar approach, but tend to rely on preprocessor macros. If you're interested, have a look at the source for Boost.Variant, which does this.

like image 170
phooji Avatar answered Oct 16 '22 05:10

phooji


You can try and do the same thing as Boost does, for example in Boost.Function (link to the template header). They use Boost.Preprocessor to enumerate various things on the given number of arguments. For example consider you want to overload a function for 0-2 arguments. The conventional way would be the following:

void foo(){...}
template<class T0>
void foo(T0 a0){...}
template<class T0, class T1>
void foo(T0 a0, T1 a1){...}

Now what Boost does, is to just put those template parameters (class T0 etc) into a preprocessor macro, use that inside the function, and then include the header 3 times for different number of arguments. Example:

// template header, no include guard
#define FOO_TEMPLATE_PARAMS BOOST_PP_ENUM_PARAMS(FOO_NUM_ARGS,class T)
#define FOO_PARAM(J,I,D) BOOST_PP_CAT(T,I) BOOST_PP_CAT(a,I)
#define FOO_PARAMS BOOST_PP_ENUM(FOO_NUM_ARGS,FOO_PARAM,BOOST_PP_EMTPY)
#if FOO_NUM_ARGS > 0
#define FOO_TEMPLATE template< FOO_TEMPLATE_PARAMS >
#else
#define FOO_TEMPLATE
#endif

FOO_TEMPLATE
void foo(FOO_PARAMS){...}

// cleanup what we've done
#undef FOO_TEMPLATE_PARAM
#undef FOO_TEMPLATE_PARAMS
#undef FOO_PARAM
#undef FOO_PARAMS
#undef FOO_TEMPLATE

The above is the template header, lets call it Foo_Template.h. Now we just include it for the number of arguments we want:

// Foo.h
#include <boost/preprocessor.hpp>
#define FOO_NUM_ARGS 0
#include "Foo_Template.h"
#define FOO_NUM_ARGS 1
#include "Foo_Template.h"
#define FOO_NUM_ARGS 2
#include "Foo_Template.h"
#define FOO_NUM_ARGS 3
#include "Foo_Template.h"
#define FOO_NUM_ARGS 4
#include "Foo_Template.h"
#undef FOO_NUM_ARGS

Perfect! With a little more preprocessor effort and boilerplate "code", we can now overload foo for any number of arguments! The preprocessor macros will expand to something like this:

// with FOO_NUM_ARGS == 0
#define FOO_TEMPLATE_PARAMS /*empty, because we enumerate from [0,FOO_NUM_ARGS)*/
#define FOO_PARAMS /*empty again*/
#define FOO_TEMPLATE /*empty, we got the 0 args version*/

void foo(){...}

// with FOO_NUM_ARGS == 1
#define FOO_TEMPLAtE_PARAMS class T0 /* BOOST_PP_ENUM is like a little for loop */
#define FOO_PARAMS T0 a0
#define FOO_TEMPLATE template< class T0 >

template< class T0 >
void foo( T0 a0 ){...}

// with FOO_NUM_ARGS == 3
#define FOO_TEMPLAtE_PARAMS class T0, class T1, class T2
#define FOO_PARAMS T0 a0, T1 a1, T2 a2
#define FOO_TEMPLATE template< class T0, class T1, class T2 >

template< class T0, class T1, class T2 >
void foo( T0 a0, T1 a1, T2 a2 ){...}

But it's a bliss we won't need this anymore with C++0x thanks to variadic templates. I love 'em.

like image 20
Xeo Avatar answered Oct 16 '22 07:10

Xeo


Here's how I make variadic templates in my code, without C++0x support and with Boost (very abridged):

// blah.hpp
// (include guards)

#ifndef BLAH_MAX_PARAMETERS
    // allow this to be changed to a higher number if needed,
    // ten is a good default number
    #define BLAH_MAX_PARAMETERS 10
#endif

#if BLAH_MAX_PARAMETERS < 0
    // but don't be stupid with it
    #error "Invalid BLAH_MAX_PARAMETERS value."
#endif

// include premade functions, to avoid the costly iteration
#include "detail/blah_premade.hpp"

// generate classes if needed
#if BLAH_MAX_PARAMETERS > BLAH_PREMADE_PARAMETERS
    #define BOOST_PP_ITERATION_LIMITS (BOSST_PP_INC(BLAH_PREMADE_PARAMETERS), \
                                        BLAH_MAX_PARAMETERS)
    #define BOOST_PP_FILENAME_1 "detail/blah.hpp"
    #include BOOST_PP_ITERATE()
#endif

That's the "main" include. As you can see, it just sets up the number of iterations desired, and makes sure that enough exist. I include a premade file because this iteration (especially when used multiple times) can really add to your compile time. I premake up to ten, so by default no iteration is done:

// detail/blah_premade.hpp
// (include guards)

// a bunch of manually made (however you want to do that)
// pastes of what our iteration normally generates

// allow this file to change the assumed count
#define BLAH_PREMADE_PARAMETERS 10

Then we have our preprocessor template:

// detail/blah.hpp
// no header guards

#define N BOOST_PP_ITERATION()

// use N to generate code

#undef

And that's it. I leave it to you to fill in the middle for whatever you want; perhaps check out Xeo's answer.

like image 24
GManNickG Avatar answered Oct 16 '22 06:10

GManNickG


In C++0x there are variadic templates.

like image 42
ognian Avatar answered Oct 16 '22 06:10

ognian