【问题标题】:How to simplify these variadic functions?如何简化这些可变参数函数?
【发布时间】:2014-07-20 07:53:23
【问题描述】:

我写了一个Logger 类来将信息转储到文件中。下面的代码是Logger的缩影。功能看起来几乎一样......但我不知道如何简化它。你能让它更优雅吗?

My earlier version 使用可变参数宏来实现这一目标。后来我注意到文件指针和缩进级别这两个数据可以封装成一个类,这样我就不需要每次都传递(FILE *fp, size_t indent)(Xml_Logger &logger) 就够了。

所以我希望我可以只使用成员函数,否则就像我在回滚......

void ind_print(const char *format, ...) {
  print_indent();

  va_list args;
  va_start(args, format);
  vprintf(format, args);
  va_end(args);
}

void ind_println(const char *format, ...) {
  print_indent();

  va_list args;
  va_start(args, format);
  vprintf(format, args);
  va_end(args);

  printf("\n");
}

void print(const char *format, ...) {
  va_list args;
  va_start(args, format);
  vprintf(format, args);
  va_end(args);
}

void println(const char *format, ...) {
  va_list args;
  va_start(args, format);
  vprintf(format, args);
  va_end(args);

  printf("\n");
}

更新:看看这两个函数。我希望println 可以调用print,但不幸的是它不能。不是吗?

void Xml_Logger::print(const char *format, ...) const {
  print_indent();
  va_list args;
  va_start(args, format);
  vfprintf(fp, format, args);
  va_end(args);
}

void Xml_Logger::println(const char *format, ...) const {
  print_indent();
  va_list args;
  va_start(args, format);
  vfprintf(fp, format, args);
  va_end(args);
  fputc('\n', fp);
}

【问题讨论】:

  • 我最近问了一个类似的问题,top voted answer 几乎就是你所做的。
  • 我的建议是避免使用宏,因为它只会增加混淆。
  • @Vorac 我不认为这是一个类似的问题...您希望在更改基础功能时尽量减少修改,但我想删除代码的冗余。
  • 使用 putchar/putc/fputc 而不是 printf 来写一个换行符
  • @WilliamPursell 我也是。每当宏出现时,人们就会批评它。所以我问的是宏以外的建议(我可以想出)。

标签: c++ variadic variadic-functions


【解决方案1】:

您可以将这些函数中的每一个简化为可变参数宏,这样您就可以摆脱对va_startva_end 的冗余调用:

#define IND_PRINT(fmt, ...) { \
    print_indent(); \
    printf(fmt, ## __VA_ARGS__); \
}

然后您应该将这些宏中的每一个都放在一个头文件中,以便项目的其余部分可以看到它们。

【讨论】:

  • 谢谢。我确实使用了这些可变宏,但people 建议我不要使用宏,而是创建一个Logger 类。
  • @Moon,你的另一个问题是关于C++,这是完全不同的故事。
  • @Vorac,哎呀,错了……我现在把标签改成了C++。谢谢。
  • @Moon,在这种情况下,IMO streams 是您的解决方案。但是,我的 C++ 很生锈,我无法给出答案。我确信Boost 提供了解决此问题的方法,该库中可能有登陆月球的代码。
【解决方案2】:

我会写这样的东西(是的,也使用宏):

打印.h:

void _print(int indent, int eol, const char *format, ...);
#define print(format, ...) _print(0, 0, format, __VA_ARGS__)
#define println(format, ...) _print(0, 1, format, __VA_ARGS__)
#define ind_print(format, ...) _print(1, 0, format, __VA_ARGS__)
#define ind_println(format, ...) _print(1, 1, format, __VA_ARGS__)

print.c:

void _print(int indent, int eol, const char *format, ...) {
  va_list args;

  if (ident) {
    print_indent();
  }

  va_start(args, format);
  vprintf(format, args);
  va_end(args);

  if (eol) {
     printf("\n");
  }
}

【讨论】:

  • 谢谢。似乎只能使用可变参数宏来转发参数。由于宏中只有一条语句,因此您无需将其包装为 do{..}while(0)
【解决方案3】:

就像你不将你的论点转发给fprintf,这是可变参数,但vfprintf,你会在你的情况下做同样的事情。

void Xml_Logger::master_print(bool indent, bool newline, const char* format, va_list& args) const
{
  if (indent) print_indent();
  vfprintf(fp, format, args);
  if (newline) fputc('\n', fp);
}

void Xml_Logger::print(const char *format, ...) const
{
  va_list args;
  va_start(args, format);
  master_print(true, false, format, args);
  va_end(args);
}

void Xml_Logger::println(const char *format, ...) const
{
  va_list args;
  va_start(args, format);
  master_printf(true, true, format, args);
  va_end(args);
}

但不幸的是,这需要在每个包装器中重复 va_list 的内容。因此,如果您有 C++11,请尝试使用 a template with perfect forwarding

【讨论】:

  • 谢谢。看来我不得不忍受va_list 的冗余。您对 C++11 中可变参数模板的回答确实令人印象深刻。这就是我想要的,即在可变参数函数之间转发参数。
【解决方案4】:

您可以使用 C++11 可变参数模板来转发可变数量的参数:

template<typename ...TArgs>
void Xml_Logger::println(const char *format, TArgs&& ...args) const
{
  print(format, std::forward(args)...);
  fputc('\n', fp);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    • 2018-05-31
    • 2021-07-31
    • 2015-01-25
    • 2014-12-12
    • 1970-01-01
    相关资源
    最近更新 更多