例:
#define FOO(...) printf(__VA_ARGS__)
#define BAR(fmt, ...) printf(fmt, __VA_ARGS__)
FOO("this works fine");
BAR("this breaks!");
Run Code Online (Sandbox Code Playgroud)
BAR()根据C99标准,上述用途确实不正确,因为它将扩展到:
printf("this breaks!",);
Run Code Online (Sandbox Code Playgroud)
请注意尾随逗号 - 不可行.
一些编译器(例如:Visual Studio 2010)将悄然摆脱那个尾随的逗号.其他编译器(例如:GCC)支持放在##前面__VA_ARGS__,如下所示:
#define BAR(fmt, ...) printf(fmt, ##__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
但有没有符合标准的方法来获得这种行为?也许使用多个宏?
现在,该##版本似乎得到了相当好的支持(至少在我的平台上),但我真的更喜欢使用符合标准的解决方案.
先发制人:我知道我可以写一个小功能.我正在尝试使用宏来做到这一点.
编辑:以下是我想要使用BAR()的一个例子(虽然简单):
#define BAR(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
BAR("here is a log message");
BAR("here is a log message with a param: %d", 42);
Run Code Online (Sandbox Code Playgroud)
这会自动为我的BAR()日志记录语句添加换行符,假设fmt它始终是双引号C字符串.它不会将换行符打印为单独的printf(),如果日志记录是行缓冲的并且异步来自多个源,则这是有利的.
简单的问题,我无法在网上找到答案.在可变参数宏中,如何查找参数的数量?如果它有解决方案,我可以使用boost预处理器.
如果它有所不同,我试图转换可变数量的宏参数来增强预处理器序列,列表或数组以进行进一步的重新处理.
考虑以下代码:
#define F(x, ...) X = x and VA_ARGS = __VA_ARGS__
#define G(...) F(__VA_ARGS__)
F(1, 2, 3)
G(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)
X = 1 and VA_ARGS = 2, 3两个宏的预期输出,这就是我用GCC得到的,但是,MSVC将其扩展为:
X = 1 and VA_ARGS = 2, 3
X = 1, 2, 3 and VA_ARGS =
Run Code Online (Sandbox Code Playgroud)
也就是说,__VA_ARGS__将其扩展为单个参数,而不是分解为多个参数.
有什么方法吗?
如何做这项工作?如何实现C99/C++ 11可变参数宏以根据给出多少参数来扩展到不同的东西?
对于debugbuilds,我通常使用Clang,因为它更好地格式化警告和错误,并使它更容易跟踪它们并修复它们.
但最近在添加了具有可变参数的宏之后,Clang告诉我以下内容(来自虚拟项目):
main.cpp:5:20: warning: named variadic macros are a GNU extension [-Wvariadic-macros]
#define stuff3(args...) stuff_i(args)
Run Code Online (Sandbox Code Playgroud)
我知道macroname(args...)在各种编译器中编译很好,包括Visualstudio,Sunstudio,当然还有GCC.但是为了确保clang是正确的,我尝试了另外两种扩展可变参数的方法:
1号:
#define stuff1(...) stuff_i(...)
Run Code Online (Sandbox Code Playgroud)
2号:
#define stuff2(...) stuff_i(__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
我都收到这条消息:
main.cpp:3:16: warning: variadic macros were introduced in C99 [-Wvariadic-macros]
Run Code Online (Sandbox Code Playgroud)
...这让我想知道Variadic宏是否实际上是C++标准的一部分(当然我知道预处理器是独立解释的)?
典型的基于LOG()宏的日志记录解决方案可能如下所示:
#define LOG(msg) \
std::cout << __FILE__ << "(" << __LINE__ << "): " << msg << std::endl
Run Code Online (Sandbox Code Playgroud)
这允许程序员使用方便且类型安全的流操作符创建数据丰富的消息:
string file = "blah.txt";
int error = 123;
...
LOG("Read failed: " << file << " (" << error << ")");
// Outputs:
// test.cpp(5): Read failed: blah.txt (123)
Run Code Online (Sandbox Code Playgroud)
问题是这会导致编译器内联多个ostream :: operator <<调用.这会增加生成的代码,从而增加函数大小,我怀疑这可能会损害指令缓存性能并阻碍编译器优化代码的能力.
这是一个"简单"替代方案,它通过调用可变参数模板函数替换内联代码:
********* 解决方案#2:VARIADIC模板功能 *********
#define LOG(...) LogWrapper(__FILE__, __LINE__, __VA_ARGS__)
// Log_Recursive wrapper that creates the ostringstream
template<typename... Args>
void LogWrapper(const char* file, int line, const Args&... …Run Code Online (Sandbox Code Playgroud) 所以我有一个在GCC中运行良好的宏,但在微软的C++编译器中没有.我希望有人可能知道一种解决方法,或者也许可以向我解释它为什么会这样.
我确定这个宏并不完全是"标准",但它确实会帮助我.
这是宏的一个功能示例:
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1)
#define FULLY_EXPANDED(count, ...) \
MAC ## count (__VA_ARGS__)
#define SEMI_EXPANDED(count, ...) FULLY_EXPANDED(count, __VA_ARGS__)
#define EXPAND_THESE(...) SEMI_EXPANDED(VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define ACTUAL_MACRO(x) parent->GetProperty<x>();
#define MAC1(a) ACTUAL_MACRO(a)
#define MAC2(a,b) MAC1(a) ACTUAL_MACRO(b)
#define MAC3(a,b,c) MAC2(a,b) ACTUAL_MACRO(c)
#define MAC4(a,b,c,d) MAC3(a,b,c) ACTUAL_MACRO(d)
#define MAC5(a,b,c,d,e) MAC4(a,b,c,d) ACTUAL_MACRO(e)
Run Code Online (Sandbox Code Playgroud)
以下是我如何使用此宏:
struct MyStructure
{
void Foo()
{
EXPAND_THESE(Property1, Property2, Property3, Property4)
}
Base * parent;
}
Run Code Online (Sandbox Code Playgroud)
以下是GCC如何扩展上述内容:
struct MyStructure
{
void …Run Code Online (Sandbox Code Playgroud) 我正在研究一个调用宏,
#define CALL(f,...) FN(f)->call((ref(new LinkedList()), __VA_ARGS__))
Run Code Online (Sandbox Code Playgroud)
在被叫时,
CALL(print,2,3,4,5);
Run Code Online (Sandbox Code Playgroud)
将2 3 4 5添加到链接列表(,重载以执行此操作)并调用print,其中需要链接列表按预期工作,但有些调用不需要参数,
CALL(HeapSize);
Run Code Online (Sandbox Code Playgroud)
它仍然需要一个链接列表,但是一个空的,上面不起作用,我试图想出一个宏,它可以使用任何一种风格吗?
编辑:挖掘gcc文档我发现在VA_ARGS之前添加## 会删除,当没有参数但是我无法嵌套宏时,
CALL(print,CALL(HeadSize));
Run Code Online (Sandbox Code Playgroud)
如果我分离它的工作调用,这会导致CALL未定义错误
据我所知,在gcc中你可以写下这样的东西:
#define DBGPRINT(fmt...) printf(fmt);
Run Code Online (Sandbox Code Playgroud)
有没有办法在VC++中做到这一点?
我知道我可以这样做:
#define MACRO(api, ...) \
bool ret = api(123, ##__VA_ARGS__);
Run Code Online (Sandbox Code Playgroud)
这只是一个例子,它是更复杂的解决方案的一部分.关键是我需要将可变数量的参数附加到第一个123. ##使编译器在123参数之后删除逗号,如果没有参数传递给MACRO.
但现在我想向api附加参数,如下:
#define MACRO(api, ...) \
bool ret = api(__VA_ARGS__##, 456);
Run Code Online (Sandbox Code Playgroud)
诺坎多.一种解决方案是使用两个宏,MACRO和MACRO_V,并使_V版本不处理任何参数.但有没有办法让它与一个宏一起工作?
variadic-macros ×10
c++ ×7
c ×4
visual-c++ ×3
gcc ×2
c++11 ×1
c99 ×1
logging ×1
overloading ×1