任何元编程方式为不同数量的模板参数生成重载?

时间:2011-03-29 15:32:13

标签: c++ templates metaprogramming

我正在尝试创建一组可以采用不同类型和数量的参数的函数模板,如下所示:

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);

[...]

在函数内对参数进行相同的处理(作为单参数模板函数的参数)。这是一个库,所以我可以接受额外的努力,如果这意味着更少的工作量或更愉快的库用户界面。

对于不同的项目,我不得不多次这样做,而且我非常厌倦必须亲自手动编写所有这些项目。当我事先不知道使用该库的项目所需的最大参数数量时,情况会变得更糟。

在我开始编写Python脚本以生成所有重载之前,是否有一些元编程方法让编译器为我做这个呢?

4 个答案:

答案 0 :(得分:6)

没有标准的方法可以做到这一点,但它可以在C ++ 0x中完成,即使用称为“可变参数模板”的技术即将推出的(但尚未官方的)标准。请参阅here以获取真实的示例。引用自己:

#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;
}

输出:(使用GCC编译,带有标记-std=c++0x

12131

从概念上讲,这类似于使用模式匹配的基本递归匹配(如在函数式语言中),但在编译时展开。

如果你不想包含一个尚未标准的功能,那么“使用python脚本的许多类似定义”方法并不是一个糟糕的方法。 boost库使用类似的方法,但往往依赖于预处理器宏。如果您有兴趣,请查看Boost.Variant的来源,这样做。

答案 1 :(得分:4)

你可以尝试和Boost做同样的事情,例如在Boost.Function中(链接到模板标题)。他们使用Boost.Preprocessor枚举给定数量的参数上的各种事物。例如,考虑要为0-2参数重载函数。传统方式如下:

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

Boost所做的就是将这些模板参数(class T0等)放入预处理器宏中,在函数内部使用它,然后为不同数量的参数包含标题3次。例如:

// 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

以上是模板标题,我们称之为Foo_Template.h。现在我们只是将它包含在我们想要的参数数量中:

// 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

完美!通过更多的预处理器工作和样板“代码”,我们现在可以为任意数量的参数重载foo!预处理器宏将扩展为如下所示:

// 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 ){...}

但是由于可变参数模板,我们不再需要C ++ 0x,这是一种幸福。我爱他们。

答案 2 :(得分:3)

以下是我在代码中制作可变参数模板的方法,没有C ++ 0x支持和Boost(非常删节):

// 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

这是“主要”包括。如您所见,它只是设置了所需的迭代次数,并确保存在足够的迭代次数。我包含一个预制文件,因为这个迭代(特别是多次使用时)可以真正增加你的编译时间。我预先制作了十个,所以默认情况下不进行迭代:

// 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

然后我们有了预处理器模板:

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

#define N BOOST_PP_ITERATION()

// use N to generate code

#undef

就是这样。我留给你填写中间的任何你想要的东西;也许看看Xeo's answer

答案 3 :(得分:1)

在C ++ 0x中有variadic templates