![](/img/trans.png)
[英]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.