函数重载,其中参数仅由省略号不同

时间:2010-09-02 00:16:25

标签: c++ variadic overloading

我有这个日志记录系统,我正在寻找一些字符串操作的快捷方式。

日志系统通过功能宏使用,然后转发到单个函数调用。例如。 #define Warning(...) LogMessage(eWarning, __VA_ARGS__);

然后,LogMessage将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”,那么“废物”就不那么多了。

编辑虽然我的例子已被“不要那样做”回答,但问题本身可以回答吗?

4 个答案:

答案 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

接受我自己的答案可能是不好的形式,即使它是回答提出的问题的答案。