2017-09-24 77 views
0

我已经实现了一个日志函数,它最终在整个代码中被完全使用。
使用MACRO获取函数参数的名称

void func(int foo, int bar){ 
    log_api_call("foo", foo, "bar",bar) 
    ... 
} 

所以我决定把它更容易和刚刚提取的变量名。
所以它会是这样的

log_api_call(foo,bar) 

甚至更​​好

log_api_call() 

,它会扩大到log_api_call("foo", foo, "bar",bar)莫名其妙。
我甚至不知道从何处开始'提取'函数变量名称。 帮助将不胜感激。

编辑:
我明白什么我以前问的是C++预处理器的功能之外,但可以C宏扩大log_api(a,b)log_api_call("a", a, "b", b)任何数量的参数? 对于定义的数字来说,作业是微不足道的。

谢谢。

回答

4

这在标准C++ 11(或标准C11-几乎与C++共享预处理器)中是不可能的。 C or C++ preprocessor不知道你的代码的AST传递给编译器(因为它在运行之前实际解析你的代码)。

我甚至不知道从哪里开始'提取'函数变量名称。

注意变量和函数只在编译时已知的(预处理后)。所以如果你想要它们,你需要在编译期间工作。在执行时,变量和函数名称一般都会丢失(并且您可执行文件可能为strip)。你可以生成你的C++代码(例如使用其他一些预处理器,如GPP或M4,或编写自己的东西)。

您可以自定义您的C++编译器(例如使用GCC MELT的扩展名或GCC插件)有log_api_call调用一些新的魔术内建(其编译器内的处理将完成大部分工作)。这需要几个月的时间,而且非常适合编译器,我不认为这是值得的。

你可以解析DWARF调试信息(这也需要几个月的时间,所以我不认为这会是明智的)。

(我想含蓄的C++代码的Linux系统上编译)

了解更多关于aspect programming

如果你想要这样强大的元编程设施,C++是错误的编程语言。阅读更多关于Common Lisp强大的宏观系统...

但C宏扩大log_api(a,b)log_api_call("a", a, "b", b)任意数量的参数?对于明确的数字,这项工作是微不足道的。

不需要。您需要一个功能更强大的预处理器来完成这项工作(或自己写)。对于特定需要,可以考虑自定义您的source code editor(例如写几百行代码的elisp这样做的提取&扩大就业在编辑时emacs)。

PS在实践中你能找到限制参数一些合理的限制

+0

为什么不宏能做到这一点?我的意思是,当然不是任何数字,但是对于合理的数字(例如少于20),您可以使用@ Hwalter的解决方案或类似的东西。 – MikeMB

+0

实际上,你的确将参数限制于例如20,也许你可以找到一些[boost](http://boost.org/)库来做这件事 –

2

一些图书馆(可能boost),它会扩大到log_api_call("foo", foo, "bar",bar)莫名其妙。

这是不可能在标准C++。

4

我想你可以从语言内达到最好的是写一个宏LOG_API_CALL(foo,bar)一个可扩展到log_api_call("foo", foo, "bar", bar)

#define LOG_API_CALL(P1,P2) log_api_call(#P1,P1,#P2,P1) 

,如果你想用一个单一的宏名支持任意多的争论这变得相当棘手,但你也可以为每个参数个数有一个单独的宏。

+0

不错的一个,只是编辑,以确切地询问未定义数量的参数 – DsCpp

+0

@DsCpp:我有那个代码说谎但它并不漂亮,而且我目前正在使用我的手机,所以我不会相信自己能够正确地复制它。 – MikeMB

2

这其实不是太难。

我建议在规范中的一个微小变化,虽然;而不是:

扩大 log_api(a,b)log_api_call("a", a, "b", b)

...它更有助于扩大类似NAMED_VALUES(a,b)"a",a,"b",b。然后你可以调用log_api(NAMED_VALUES(a,b)),但是你的log_api可以保持更通用(例如,log_api(NAMED_VALUES(a,b),"entering function")是可能的)。这种方法还可以避免关于零参数案件的很多复杂问题。

// A preprocessor argument counter 
#define COUNT(...) COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,) 
#define COUNT_I(_9,_8,_7,_6,_5,_4,_3,_2,_1,X,...) X 
// Preprocessor paster 
#define GLUE(A,B) GLUE_I(A,B) 
#define GLUE_I(A,B) A##B 
// chained caller 
#define NAMED_VALUES(...) GLUE(NAMED_VALUES_,COUNT(__VA_ARGS__))(__VA_ARGS__) 
// chain 
#define NAMED_VALUES_1(a) #a,a 
#define NAMED_VALUES_2(a,...) #a,a,NAMED_VALUES_1(__VA_ARGS__) 
#define NAMED_VALUES_3(a,...) #a,a,NAMED_VALUES_2(__VA_ARGS__) 
#define NAMED_VALUES_4(a,...) #a,a,NAMED_VALUES_3(__VA_ARGS__) 
#define NAMED_VALUES_5(a,...) #a,a,NAMED_VALUES_4(__VA_ARGS__) 
#define NAMED_VALUES_6(a,...) #a,a,NAMED_VALUES_5(__VA_ARGS__) 
#define NAMED_VALUES_7(a,...) #a,a,NAMED_VALUES_6(__VA_ARGS__) 
#define NAMED_VALUES_8(a,...) #a,a,NAMED_VALUES_7(__VA_ARGS__) 
#define NAMED_VALUES_9(a,...) #a,a,NAMED_VALUES_8(__VA_ARGS__) 

这最多支持9个参数,但应该很容易看到如何扩展到更多。

+0

只是好奇:这是否也适用于msvc?他们的预处理器处理可变宏参数有点不同。 – MikeMB

+0

@MikeMB不,它不会。 [这是一种方法](https://godbolt.org/g/NB8B8x);不幸的是,它不能使用相同类型的模式(在MSVC中像这样“链接”需要使用“CALL”宏来防止__VA_ARGS__被折叠为单个参数;但是因为它使用'CALL'蓝色运行,所以不能巢)。 –