如何在同一个参数列表上多次调用 vsprintf?

问题描述 投票:0回答:1

我想要一个可以这样调用的函数:

    my_printf(int unimportant,
              "%-10s", "a string",
              "%5.1f", 1.23,
              format_string, value_to_format,
              /* possibly more pairs like this */
              (char *) NULL);

我们可以假设每个格式字符串最多包含一个

%
转义序列。

我想使用

sprintf
系列之一来格式化值,也许会产生类似
"a string  ,   1.2, …"
的结果。

到目前为止我知道几种行不通的方法。 以下代码显示了基本思想(不完整的草图):

      char * my_printf(int uninmportant, ...)
      {
         va_arg ap;
         va_start(ap, unimportant);
         
         while (1) {
           char *format = va_arg(ap, char *);
           if (!format) break;
           vsprintf(buffer, format, ap);
           /* append buffer somewhere */
         }
       }

这不起作用,因为无法保证从

ap
返回时
vsprintf
的有用性。 它可能指向任何地方,也可能指向任何地方。 通常的解决方法是在传递之前将
ap
va_copy
复制。 但这在这种情况下没有帮助:

      char * my_printf(int uninmportant, ...)
      {
         va_arg ap;
         va_start(ap, unimportant);
         
         while (1) {
           char *format = va_arg(ap, char *);
           if (!format) break;
           va_arg ap_copy;
           va_copy(ap_copy, ap);
           vsprintf(buffer, format, ap_copy);
           /* append buffer somewhere */
           /* ?? seek ap forward to point to the next format string ?? */
         }
       }

现在

vsprintf
不再破坏
ap
。 但是“向前寻求
ap
以指向下一个格式字符串”似乎相当困难,因为它似乎涉及解析和解释
format
的内容,至少足以清楚地知道要传递给
va_arg
什么类型。

同样,如果我尝试使用

sprintf
解决问题,我仍然必须解析格式字符串才能理解如何调用
sprintf
:

      char * my_printf(int uninmportant, ...)
      {
         va_arg ap;
         va_start(ap, unimportant);
         
         while (1) {
           char *format = va_arg(ap, char *);
           if (!format) break;
           ??TYPE?? arg = va_arg(ap, ??TYPE??);
           sprintf(buffer, format, arg);
           /* append buffer somewhere */
         }
       }

再次,解决这个问题的唯一方法似乎是解析

format
的值,然后为每种可能的类型提供一个带有手臂的大
switch

我不可能是第一个或第一千个想要这样做的人。 这里的传统观念是什么?

  1. 以下是您忽略的内容:……
  2. 你运气不好,众所周知,没有便携式解决方案
  3. 执行此操作的唯一方法是解析格式字符串
  4. 您可以使用预处理器玩一个肮脏的把戏,如下所示:……
  5. 以下代码于 1982 年发表在贝尔系统技术期刊上,很有帮助,每个人都使用它的一些变体:……
  6. (还有什么?)

我知道某些编译器(例如 GCC)提供了不可移植的扩展来协助实现此目的,例如

parse_printf_format
,但我想要一个更可移植的解决方案。

感谢您的任何建议。

c variadic-functions
1个回答
0
投票

但是“向前寻找

ap
以指向下一个格式字符串”似乎相当困难,因为它似乎涉及解析和解释
format
的内容,至少足以清楚地知道要传递给
va_arg
什么类型。

是的,如果不知道(足够接近)每个元素的类型,您就无法明确地推进

va_list
的元素。 这是 C 可变参数函数和 stdarg.h 宏的基本限制。

解决这个问题的唯一方法似乎是解析格式的值,然后为每种可能的类型设置一个带有手臂的大开关。

是的,这非常正确。 这项工作有点简单,因为

  • 参数将进行左值转换,这会影响所有类型限定符

  • 默认参数升级将对参数值执行,这减少了您需要考虑的类型数量

  • 您只需要兼容类型,不一定完全匹配,尽管在实践中,上述几点可能没有意义。

  • 有一些例外情况,甚至需要兼容的类型,这也可能会减少您需要考虑的类型数量(请参阅 C23 7.16.2.2./2)

  • 您的问题域也可能限制您需要考虑的类型数量。 例如,

    printf
    系列函数不支持任意指针,而仅支持指向
    char
    的指针和指向
    void
    的指针,并且这两个可以与 stdarg 宏互换使用。 在某些情况下(与支持的实际参数值相关),您可能能够将无符号整数类型处理为相应的有符号整数类型,反之亦然。

但是,最终您需要应用所传递参数类型的知识才能继续浏览列表。 这不仅仅是参数大小,还考虑了完全不同的参数传递机制的可能性,例如可以传入哪些寄存器(如果有的话)不同类型的参数。对此没有标准的快捷方式。

© www.soinside.com 2019 - 2024. All rights reserved.