简体   繁体   English

如何使用宏或元编程在 C++ 中创建“passthru”函数?

[英]How can one make a 'passthru' function in C++ using macros or metaprogramming?

So I have a series of global functions, say:所以我有一系列全局函数,比如:

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

I want to make a C++ wrapper around these, something like:我想围绕这些制作一个 C++ 包装器,例如:

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

There's about 40 functions like this, 35 of them I just want to pass through to the global function, the other 5 I want to do something different with.大约有 40 个这样的函数,其中 35 个我只想传递给全局函数,另外 5 个我想做一些不同的事情。

Ideally the implementation of MyFoo.cpp would be something like:理想情况下,MyFoo.cpp 的实现类似于:

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

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

But I'm having trouble figuring out an elegant way to make the above PASSTHRU macro.但是我很难找到一种优雅的方式来制作上述 PASSTHRU 宏。

What I really need is something like the mythical X getArgs() below:我真正需要的是像下面神话般的 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 
}

But short of dropping into assembly I can't find a good implementation of getArgs().但是没有进入程序集,我找不到 getArgs() 的良好实现。

You could use Boost.Preprocessor to let the following:您可以使用Boost.Preprocessor来执行以下操作:

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

... expand to: ...扩展为:

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

... using these macros: ...使用这些宏:

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

At 40-odd functions, you could type the wrappers out by hand in an hour.在 40 多个函数中,您可以在一个小时内手动输入包装器。 The compiler will check the correctness of the result.编译器将检查结果的正确性。 Assume an extra 2 minutes for each new function that needs wrapping, and an extra 1 minute for a change in signature.假设每个需要包装的新函数需要额外 2 分钟,而签名更改需要额外 1 分钟。

As specified, and with no mention of frequent updates or changes, it doesn't sound like this problem requires a cunning solution.正如指定的那样,并且没有提到频繁的更新或更改,听起来这个问题并不需要一个狡猾的解决方案。

So, my recommendation is to keep it simple: do it by hand.所以,我的建议是保持简单:手工操作。 Copy prototypes into source file, then use keyboard macros (emacs/Visual Studio/vim) to fix things up, and/or multiple passes of search and replace, generating one set of definitions and one set of declarations.将原型复制到源文件中,然后使用键盘宏 (emacs/Visual Studio/vim) 进行修复,和/或多次搜索和替换,生成一组定义和一组声明。 Cut declarations, paste into header.剪切声明,粘贴到标题中。 Fill in definitions for the non-passing-through functions.填写非传递函数的定义。 This won't win you any awards, but it'll be over soon enough.这不会为您赢得任何奖项,但很快就会结束。

No extra dependencies, no new build tools, works well with code browsing/tags/intellisense/etc., works well with any debugger, and no specialized syntax/modern features/templates/etc., so anybody can understand the result.没有额外的依赖项,没有新的构建工具,可以很好地与代码浏览/标签/智能感知/等一起使用,可以很好地与任何调试器一起使用,并且没有专门的语法/现代功能/模板/等,因此任何人都可以理解结果。 (It's true that nobody will be impressed -- but it will be the good kind of unimpressed.) (确实没有人会留下深刻的印象——但这将是一种不为所动的好。)

Slightly different syntax but...语法略有不同,但...

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

You could get something closer to yours if you use tuples, but you'd have to supply the arg count to the base function (you can't derive a size from a tuple).如果你使用元组,你可以得到更接近你的东西,但你必须将 arg 计数提供给基本函数(你不能从元组派生大小)。 Sort of like so:有点像这样:

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

That about what you're looking for?那是关于你在寻找什么? I knew it could be done;我知道这是可以做到的; took about a half hour to solve.花了大约半个小时来解决。 You seem to expect that there's an implicit 'this' that has to be gotten rid of but I don't see why...so maybe I misunderstand the problem.您似乎期望必须摆脱一个隐含的“this”,但我不明白为什么......所以也许我误解了这个问题。 At any rate, this will let you quickly make default "passthrough" member functions that defer to some global function.无论如何,这将让您快速创建默认的“直通”成员函数,这些函数遵循某些全局函数。 You'll need a DECPASSTHROUGH for the class declaration if you want to skip having to declare them all...or you could modify this to make inline functions.如果您想跳过必须全部声明它们,您将需要一个 DECPASSTHROUGH 类声明......或者您可以修改它以创建内联函数。

Hint: Use BOOST_PP_STRINGIZE((XX)) to test the output of preprocessor metafunctions.提示:使用 BOOST_PP_STRINGIZE((XX)) 来测试预处理器元函数的输出。

My initial thought, and this probably won't work or others would have stated this, is to put all your base functions together in a class as virtual.我最初的想法,这可能行不通,或者其他人会这样说,是将所有基本函数放在一个类中作为虚拟。 Then, write the functionality improvements into inherited classes and run with it.然后,将功能改进写入继承的类并使用它运行。 It's not a macro wrapper, but you could always call the global functions in the virtual classes.它不是宏包装器,但您始终可以在虚拟类中调用全局函数。

With some assembly trickery, you could probably do exactly what you'd want, but you would lose portability more than likely.使用一些组装技巧,您可能完全可以做您想要的,但您很可能会失去可移植性。 Interesting question and I want to hear other's answers as well.有趣的问题,我也想听听其他人的答案。

You may want to use a namespace if you want to not deal with class stuff, like this .如果你不想处理类的东西,你可能想要使用namespace ,比如this You could also use static member methods in a class, but I think that people don't like that anymore.您也可以在类中使用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

Or或者

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

Perfect forwarding relies on rvalue references.完美转发依赖于右值引用。 STL has a blog entry on it at Link and you would want to choose a compiler that supported the feature to take this approach. STL 在Link上有一个博客条目,您可能希望选择支持该功能的编译器来采用这种方法。 He's discussing Visual C++ 2010.他正在讨论 Visual C++ 2010。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM