我有这个日志记录系统,我正在寻找一些字符串操作的快捷方式。
日志系统通过功能宏使用,然后转发到单个函数调用。例如。 #define Warning(...) LogMessage(eWarning, __VA_ARGS__);
。
snprintf
放入新缓冲区,然后将该消息提供给碰巧安装的任何日志目标; printf,OutputDebugString等
不幸的是,我遇到了一个问题,即我们所拥有的缓冲区不够大,因此输出会被截断。我还意识到,如果输出消息中包含百分比符号,则此方法将失败,因为snprintf将尝试处理va_args。最后,由于我们的大多数日志消息都没有使用va_args,因此复制字符串只是为了将它呈现给记录器似乎很愚蠢。
所以给定我的函数原型,我应该能够根据省略号的存在来重载吗?换句话说,我是否可以假设我可以做类似的事情:
LogMessage(LogLevel, const char* message, ...);
LogMessage(LogLevel, const char* message);
我的谷歌尝试没有产生任何特别有用的东西(只是告诉我椭圆将匹配,如果没有别的,根据我的要求没有匹配),我刚刚开始实施给了我一个模糊的函数调用错误。
有了错误,我应该接受我不能这样做,但我想知道它是否只是我正在使用的编译器,或者我是否做错了。我可以用
达到类似的效果// edited version of what I really have to remove our local APIs,
// please excuse minor errors
const char* message = NULL;
char buffer[512];
va_list args;
va_start(args, format);
if(strcmp(format, "%s") == 0) {
message = va_arg(args, const char*);
}
else if (strchr(format, '%') == NULL) {
message = format;
}
else {
vsnprintf(buffer, 512, format, args);
message = buffer;
}
va_end(args);
......但在典型情况下这似乎很浪费,只需通过传递的参数数量即可知道。例如。如果省略号与任何东西不匹配,请选择其他函数?如果这不起作用,是否有另一种我可以尝试的方法,不需要用户决定使用宏名称将调用哪个函数?老实说,一旦我意识到,如果有人在他们的日志消息中随意地说Error("Buffer not 100% full");
并且因此得到“缓冲区不是1007.732873e10ull”,那么“废物”就不那么多了。
编辑虽然我的例子已被“不要那样做”回答,但问题本身可以回答吗?
答案 0 :(得分:3)
我还意识到如果输出消息中包含百分号,则此方法将失败,因为snprintf将尝试处理va_args。
然后来电者要小心。如果您的函数被记录为采用printf样式的格式字符串,那么调用者有责任逃避任何百分号。尝试处理无效的格式字符串并不是你的工作。
老实说,一旦我意识到如果有人在他们的日志消息中随意地说
Error("Buffer not 100% full");
并且因此得到“缓冲区不是1007.732873e10ull”,那么“废物”就没那么多了。
我认为你最好不要遵循C ++的精神。在Java方法中,通常检查有效参数并在传递无效值时抛出异常。在C ++中,你只需让来电者自拍。让他们写100%%
比跳过篮球更好,以防止他们学习如何正确调用你的功能。
答案 1 :(得分:2)
在C ++ 11中,您可以使用具有单参数情况的显式特化的可变参数模板:
void bar(int a, ...) {
// va_list stuff
}
template <typename... T>
void foo(int a, T... args) { // (1)
bar(a, args...); // or do all the vararg stuff here directly
}
template <>
void foo(int a) { // (2)
printf("single\n");
}
然后:
//foo(); // compile error, as expected
foo(1); // uses (2)
foo(2,1); // uses (1)
foo(3,1,"asdf"); // uses (1)
...
答案 2 :(得分:2)
我对这个问题的原始答案感到鼓舞,但却略有改进。
static void LogMessage(LogLevel level, const char* message);
template <typename T>
static void LogMessage(LogLevel level, const char* format, T t, ...)
{
LogMessageVA(level, format, (va_list)&t);
}
static void LogMessageVA(LogLevel level, const char* format, va_list argptr);
这不需要“假设”第二个参数是const char *。
答案 3 :(得分:1)
好的,我想我想出了一个问题的解决方案。
事实是,不能过载仅基于是否存在省略号的参数。即你不能拥有签名只会因椭圆的存在而变化的函数。
但是,如果我从省略号原型中删除const char*
参数,那么 可能会像我所问的那样。即。
LogMessage(LogLevel, ...);
LogMessage(LogLevel, const char* message);
是明确的,但现在你要与你必须假设第一个参数是const char*
的事实作斗争,但它可能不是。以约翰库格曼的建议为例,也许那很好;您记录允许的参数和用户要注意的参数。如果只有const char*
,则会调用非省略号函数,如果还有其他任何内容,包括记录的const char*
后跟一些参数,则会调用省略号函数。
不幸的是,似乎这是一个可能的解决方案的范围,它允许您将va_args传递给子函数,在我的示例中为vsnprintf
。
接受我自己的答案可能是不好的形式,即使它是回答提出的问题的答案。