簡體   English   中英

如何使用宏或元編程在 C++ 中創建“passthru”函數?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM