【问题标题】:Any metaprogramming way to generate overloads for various numbers of template parameters?任何元编程方法可以为各种数量的模板参数生成重载?
【发布时间】:2011-03-29 15:32:13
【问题描述】:

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

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 脚本来生成所有重载之前,是否有一些元编程方法可以让编译器代替我完成它?

【问题讨论】:

  • 我认为元编程是不可能的,或者不会有用于此的 boost 预处理器库。 boost.org/doc/libs/1_46_1/libs/preprocessor/doc
  • 请注意,如果您使用通过引用传递的参数调用此类函数,则类型的引用部分将被删除。所以“const std::string &”将变成“const std::string”。避免这种情况的一个简单方法是让模板函数通过引用获取其参数。例如:调用(T0 & arg0);
  • 谢谢大家。不幸的是,我不能为此使用 C++0x,但看起来我可以滥用 Boost.Preprocessor 来做我需要的事情。

标签: c++ templates metaprogramming


【解决方案1】:

没有执行此操作的标准方法,但可以在 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 的源代码,它就是这样做的。

【讨论】:

  • s/but not-yet-official/nearly finished, already 80% implemented in major compilers/.
  • @delnan:我同意,但在可变参数模板(如 Microsoft Visual Studio 2010 :-/ )的情况下有明显的排除。
【解决方案2】:

你可以尝试做和 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 中不再需要它了。我爱他们。

【讨论】:

  • 为什么不使用BOOST_PP_ITERATE
  • 您在什么时候实际使用boost/preprocessor.hpp 包含?
  • @GMan:我没有进一步研究BOOST_PP_ITERATE。我知道可以用它来迭代包含,但我不会展示我至少有一半信心的东西。但请随时编辑和添加该版本。
  • @phooji: BOOST_PP_ENUM, BOOST_PP_CAT
【解决方案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

【讨论】:

    【解决方案4】:

    在 C++0x 中有variadic templates

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-25
      • 2019-06-08
      • 2010-10-17
      • 2017-07-21
      • 1970-01-01
      • 1970-01-01
      • 2021-09-28
      相关资源
      最近更新 更多