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?
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.
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.
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.
In C++0x there are variadic templates.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With