参数仅由省略号不同的函数重载

发布于 2024-09-17 07:19:38 字数 1468 浏览 4 评论 0原文

我有这个日志系统,我希望能简化一些字符串操作。

日志系统通过功能宏使用,然后转发到单个函数调用。例如#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);

我的谷歌尝试没有产生任何特别有用的东西(只是告诉我,如果没有其他东西,省略号将匹配,这与我的要求不同没有< /em> 匹配),而我最初尝试实现只是给了我一个不明确的函数调用错误。

有了这个错误,我应该接受我不能这样做,但我想知道这是否只是我正在使用的编译器或者我是否做错了。我可以用 ... 实现类似的效果

// 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"); 并得到“Buffer not 1007.732873e10ull”时,“浪费”就不再那么重要了一个结果。

编辑:虽然我的示例的答案是“不要这样做”,但问题本身可以得到回答吗?

I've got this logging system for which I'm looking to shortcut some of the string manipulation.

The logging system is used via functional macros which then forward to a single function call. E.g. #define Warning(...) LogMessage(eWarning, __VA_ARGS__);.

LogMessage then does a snprintf into a new buffer and then presents that message to whatever log targets happen to be installed; printf, OutputDebugString, etc.

Unfortunately, I've run into a problem where the buffer that we have isn't big enough, so the output gets truncated. I also realized that this method will fail if the output message has percent symbols in it, as snprintf will try to process the va_args. Finally, as the majority of our log messages do not use the va_args, it seems silly to copy the string just to present it to the loggers.

So- given my function prototype, should I be able to overload based on the presence of the ellipses? In other words, should I be able to assume that I can do something like:

LogMessage(LogLevel, const char* message, ...);
LogMessage(LogLevel, const char* message);

My google attempts haven't yielded anything particularly useful (just showing me that ellipses will match if nothing else does, varying from my requirements that nothing matches), and my initial stab at an implementation just gave me an ambiguous function call error.

With the error, I should just accept that I can't do this, but I'm wondering if it's just the compiler I'm using or if maybe I'm doing it wrong. I can achieve a similar effect with

// 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);

...but this seems wasteful in the typical case which can be known simply by the number of parameters being passed. E.g. if ellipses don't match anything, select the other function? If this doesn't work, is there another method I can try that doesn't require the user to decide with the macro name which function will be called? Honestly it's not even as much about the "waste" once I realized that if someone haphazardly said Error("Buffer not 100% full"); in their log message and got "Buffer not 1007.732873e10ull" as a result.

Edit: While my example has been answered by a "don't do that," can the question itself be answered?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

那请放手 2024-09-24 07:19:38

我受到这个问题最初答案的启发,但提出了一些改进。

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* 即可工作。

I was inspired by the original answer to this question, but have come up with a slight improvement.

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);

This works without having to 'assume' that the second argument is const char*.

傲世九天 2024-09-24 07:19:38

我还意识到,如果输出消息中包含百分比符号,此方法将会失败,因为 snprintf 将尝试处理 va_args。

那么来电者要小心。如果您的函数被记录为采用 printf 样式格式字符串,那么调用者有责任转义任何百分号。尝试处理无效的格式字符串实际上并不是您的工作。

老实说,当我意识到如果有人在日志消息中随意地说 Error("Buffer not 100% full"); 并得到“Buffer not 1007.732873”时,“浪费”就不再那么重要了结果是 e10ull”。

我认为你最好遵循 C++ 精神。在 Java 方法中,通常会检查参数是否有效,并在传递无效值时引发异常。在 C++ 中,你只是让调用者搬起石头砸自己的脚。让他们写 100%% 比为了保护他们学习如何正确调用你的函数而跳圈要好。

I also realized that this method will fail if the output message has percent symbols in it, as snprintf will try to process the va_args.

Then caller beware. If your function is documented to take printf-style format strings then it is the caller's responsibility to escape any percent signs. It is not really your job to attempt to handle invalid format strings.

Honestly it's not even as much about the "waste" once I realized that if someone haphazardly said Error("Buffer not 100% full"); in their log message and got "Buffer not 1007.732873e10ull" as a result.

I think you're better off going along with the C++ ethos. In Java methods commonly check for valid arguments and throw exceptions when passed invalid values. In C++ you simply let callers shoot themselves in the foot. It's better to have them write 100%% than to jump through hoops to protect them from learning how to call your function properly.

_蜘蛛 2024-09-24 07:19:38

在 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)
...

In C++11 you can use variadic templates with an explicit specialization for the single-argument case:

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");
}

Then:

//foo();    // compile error, as expected
foo(1);     // uses (2)
foo(2,1);   // uses (1)
foo(3,1,"asdf"); // uses (1)
...
别想她 2024-09-24 07:19:38

好吧,我想我想出了这个问题的解决方案。

事实上,您不能仅根据是否有省略号参数来进行重载。即,您不能拥有仅因省略号的存在而变化的签名的函数。

但是,如果我从省略号原型中删除 const char* 参数,就可以执行类似我所要求的操作。 Ie

LogMessage(LogLevel, ...);
LogMessage(LogLevel, const char* message);

是明确的,但现在您必须面对这样一个事实:您必须假设第一个参数是 const char*,但很可能不是。采纳约翰·库格曼的建议,也许这样就可以了。您记录允许的参数并用户注意。如果只有 const char*,则将调用非省略号函数;如果有任何其他内容,包括已记录的 const char*,则将调用省略号函数接下来是一些参数。

不幸的是,这似乎是允许您将 va_args 传递给子函数的可能解决方案的范围,在我的示例中传递给 vsnprintf

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

Ok I think I came up with a solution for the question.

The fact is that you can't overload based only on whether there are parameters for the ellipses or not. I.e. you can't have functions which have signatures that vary only on the presence of the ellipses.

However, it is possible to do something like what I was asking if I drop the const char* parameter from the ellipses prototype. I.e.

LogMessage(LogLevel, ...);
LogMessage(LogLevel, const char* message);

is unambiguous, but now you battle with the fact that you have to assume that the first parameter is a const char*, but it may well not be. Taking John Kugelman's advice, maybe that's fine; you document the parameters that are allowed and user beware. The non-ellipses function will be called if there is only a const char*, and the ellipses function will be called if there is any thing else including the documented const char* followed by some number of parameters.

Unfortunately it seems that this is the extent of a possible solution that allows you to pass the va_args on to child functions, in my example case to vsnprintf.

It's probably bad form to accept my own answer, even though it's the one that answers the presented question.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文