[英]Extracting C Function's Parameters Using C++ Metaprogramming (Example from “Practical C++ Metaprogramming”)
[英]How can one make a 'passthru' function in C++ using macros or 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() 的良好实现。
您可以使用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) \
); \
}
在 40 多个函数中,您可以在一个小时内手动输入包装器。 编译器将检查结果的正确性。 假设每个需要包装的新函数需要额外 2 分钟,而签名更改需要额外 1 分钟。
正如指定的那样,并且没有提到频繁的更新或更改,听起来这个问题并不需要一个狡猾的解决方案。
所以,我的建议是保持简单:手工操作。 将原型复制到源文件中,然后使用键盘宏 (emacs/Visual Studio/vim) 进行修复,和/或多次搜索和替换,生成一组定义和一组声明。 剪切声明,粘贴到标题中。 填写非传递函数的定义。 这不会为您赢得任何奖项,但很快就会结束。
没有额外的依赖项,没有新的构建工具,可以很好地与代码浏览/标签/智能感知/等一起使用,可以很好地与任何调试器一起使用,并且没有专门的语法/现代功能/模板/等,因此任何人都可以理解结果。 (确实没有人会留下深刻的印象——但这将是一种不为所动的好。)
语法略有不同,但...
#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*))
那是关于你在寻找什么? 我知道这是可以做到的; 花了大约半个小时来解决。 您似乎期望必须摆脱一个隐含的“this”,但我不明白为什么......所以也许我误解了这个问题。 无论如何,这将让您快速创建默认的“直通”成员函数,这些函数遵循某些全局函数。 如果您想跳过必须全部声明它们,您将需要一个 DECPASSTHROUGH 类声明......或者您可以修改它以创建内联函数。
提示:使用 BOOST_PP_STRINGIZE((XX)) 来测试预处理器元函数的输出。
我最初的想法,这可能行不通,或者其他人会这样说,是将所有基本函数放在一个类中作为虚拟。 然后,将功能改进写入继承的类并使用它运行。 它不是宏包装器,但您始终可以在虚拟类中调用全局函数。
使用一些组装技巧,您可能完全可以做您想要的,但您很可能会失去可移植性。 有趣的问题,我也想听听其他人的答案。
如果你不想处理类的东西,你可能想要使用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__)
...
完美转发依赖于右值引用。 STL 在Link上有一个博客条目,您可能希望选择支持该功能的编译器来采用这种方法。 他正在讨论 Visual C++ 2010。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.