如何使用宏或元编程在C ++中创建'passthru'功能?

时间:2010-05-24 19:43:02

标签: c++ macros metaprogramming

所以我有一系列全局函数,比如说:

foo_f1(int a, int b, char *c);
foo_f2(int a);
foo_f3(char *a);

我想围绕这些做一个C ++包装,比如:

MyFoo::f1(int a, int b, char* c); 
MyFoo::f2(int a);
MyFoo::f3(char* a);

大约有40个这样的函数,其中35个我只想传递给全局函数,另外5个我想做一些不同的函数。

理想情况下,MyFoo.cpp的实现类似于:

PASSTHRU( f1, (int a, int b, char *c) );
PASSTHRU( f2, (int a) );

MyFoo::f3(char *a)
{
   //do my own thing here
}

但是我无法找出制作上述PASSTHRU宏的优雅方法。

我真正需要的是像下面的神秘X getArgs():

MyFoo::f1(int a, int b, char *c)
{
  X args = getArgs();
  args++; //skip past implicit this..
  ::f1(args);  //pass args to global function 
}

但是由于没有进入汇编,我找不到getArgs()的良好实现。

6 个答案:

答案 0 :(得分:10)

您可以使用Boost.Preprocessor进行以下操作:

struct X {
    PASSTHRU(foo, void, (int)(char))
};

...扩展为:

struct X {
    void foo ( int arg0 , char arg1 ) { return ::foo ( arg0 , arg1 ); }
};

...使用这些宏:

#define DO_MAKE_ARGS(r, data, i, type) \
  BOOST_PP_COMMA_IF(i) type arg##i

#define PASSTHRU(name, ret, args) \
  ret name ( \
    BOOST_PP_SEQ_FOR_EACH_I(DO_MAKE_ARGS, _, args) \
  ) { \
    return ::name ( \
      BOOST_PP_ENUM_PARAMS(BOOST_PP_SEQ_SIZE(args), arg) \
    ); \
  }

答案 1 :(得分:5)

在40多个功能中,您可以在一小时内手动输入包装纸。编译器将检查结果的正确性。假设需要换行的每个新功能额外需要2分钟,签名更改需要额外1分钟。

根据规定,并且没有提及频繁的更新或更改,听起来这个问题并不需要狡猾的解决方案。

所以,我的建议是保持简单:手工完成。将原型复制到源文件中,然后使用键盘宏(emacs / Visual Studio / vim)进行修复,和/或多次搜索和替换,生成一组定义和一组声明。剪切声明,粘贴到标题中。填写非传递函数的定义。这不会赢得任何奖项,但它很快就会结束。

没有额外的依赖,没有新的构建工具,适用于代码浏览/标签/ intellisense /等,适用于任何调试器,没有专门的语法/现代功能/模板/等,所以任何人都可以理解结果。 (确实没有人会留下深刻的印象 - 但这将是一种不受欢迎的好事。)

答案 2 :(得分:2)

语法略有不同但......

#include <boost/preprocessor.hpp>
#include <iostream>

void f1(int x, int y, char* z) { std::cout << "::f1(int,int,char*)\n"; }

#define GENERATE_ARG(z,n,unused) BOOST_PP_CAT(arg,n)
#define GET_ARGS(n)  BOOST_PP_ENUM(n, GENERATE_ARG, ~)

#define GENERATE_PARAM(z,n,seq) BOOST_PP_SEQ_ELEM(n,seq) GENERATE_ARG(z,n,~)

#define GENERATE_PARAMS(seq) BOOST_PP_ENUM( BOOST_PP_SEQ_SIZE(seq), GENERATE_PARAM, seq )

#define PASSTHROUGH(Classname, Function, ArgTypeSeq) \
  void Classname::Function( GENERATE_PARAMS(ArgTypeSeq) ) \
{ \
  ::Function( GET_ARGS( BOOST_PP_SEQ_SIZE(ArgTypeSeq) ) ); \
}

struct test
{
  void f1(int,int,char*);
};

PASSTHROUGH(test,f1,(int)(int)(char*))

int main()
{
  test().f1(5,5,0);

  std::cin.get();
}

如果你使用元组,你可以得到更接近你的东西,但是你必须向基函数提供arg计数(你不能从元组中获得大小)。有点像这样:

PASSTHROUGH(test,f1,3,(int,int,char*))

关于你在寻找什么?我知道可以做到;花了大约半个小时才解决。你似乎期望有一个隐含的'这个'必须被摆脱,但我不明白为什么......所以也许我误解了这个问题。无论如何,这将允许您快速制作默认的“passthrough”成员函数,这些函数遵循某些全局函数。如果你想跳过必须全部声明它们,你需要一个DECPASSTHROUGH用于类声明......或者你可以修改它来制作内联函数。

提示:使用BOOST_PP_STRINGIZE((XX))测试预处理器元函数的输出。

答案 3 :(得分:0)

我最初的想法,这可能不起作用或其他人会说明这一点,就是将所有基本功能放在一个类中作为虚拟。然后,将功能改进写入继承的类并使用它运行。它不是宏包装器,但您总是可以在虚拟类中调用全局函数。

有了一些装配技巧,你可能会完全按照自己的意愿行事,但是你可能会失去可移植性。有趣的问题,我也希望听到其他人的答案。

答案 4 :(得分:0)

如果您不想处理课程内容,可能需要使用namespace,例如this。你也可以在课堂上使用static成员方法,但我认为人们不再那样了。

#ifndef __cplusplus
#define PASSTHRU(type, prefix, func, args)  type prefix##_##func args
#else
#define PASSTHRU(type, prefix, func, args)  type prefix::func args
#endif

或者

#ifndef __cplusplus
#define PASSTHRU(type, prefix, func, ...)  type prefix##_##func(__VA_ARGS__)
...

答案 5 :(得分:0)

完美转发依赖于右值引用。 STL在http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx上有一个博客条目,您可能希望选择支持该功能的编译器来采用这种方法。他正在讨论Visual C ++ 2010。