gcc:混合-Wformat和register_printf_specifier

时间:2015-03-20 06:09:01

标签: c++ c gcc printf

我想使用自定义printf格式标志(通过gcc的register_printf_specifier),同时仍然获得-Wformat-Werror编译器标志的好处。

在下面的示例中,我设法使用自定义%W标记打印我的小部件,但我必须禁用-Wformat-Wformat-extra-args

#pragma GCC diagnostic ignored "-Wformat"
#pragma GCC diagnostic ignored "-Wformat-extra-args"

是否有可能拥有两全其美的优势?

我想我的问题归结为:有没有办法用-Wformat注册格式说明符和关联类型?

实施例

#include <cstdio>
#include <cstdlib>
#include <printf.h>

struct Widget
{
    const char* name;
};

int print_widget(FILE* stream, const struct printf_info* info, const void* const* args)
{
    const Widget* widget = *((const Widget**)(args[0]));
    return fprintf(stream, "%*s", (info->left ? -info->width : info->width), widget->name);
}

int print_widget_arginfo(const struct printf_info*, size_t n, int* argtypes, int* size)
{
    if (n > 0)
    {
        argtypes[0] = PA_POINTER;
        size[0] = sizeof (Widget *);
    }
    return 1;
}

int main(void)
{
    register_printf_specifier('W', print_widget, print_widget_arginfo);

    Widget widget { "foobar" };

    #pragma GCC diagnostic ignored "-Wformat"
    #pragma GCC diagnostic ignored "-Wformat-extra-args"

    printf("|%W|\n", &widget);
    printf("|%35W|\n", &widget);
    printf("|%-35W|\n", &widget);
}

输出:

|foobar|
|                             foobar|
|foobar                             |

2 个答案:

答案 0 :(得分:2)

目前没有这种方式。请参阅this gcc bug

太难难以修改GCC以暴露其printf检查的一些内部机制,从而可以编写一个了解您的添加内容的GCC插件。它确实需要对GCC有点熟悉。

答案 1 :(得分:1)

虽然没有直接的方法来针对-Wformat注册新的说明符,但有一种解决方法。您可以重载%p本身并检测它是否应该是Widget*。一种方法是将特殊值编码为宽度,并让您的说明符代码检测特殊值。例如,

#define IS_WIDGET(INFO) (((INFO)->width & MASK) == WIDGET)

int print_widget(FILE* stream, const struct printf_info* info,
                 const void* const* args)
{
    if (IS_WIDGET(info)) return print_widget_impl(stream, info, args);
    return fprintf(stream, "%#*llx",
                   (info->left ? -info->width : info->width),
                   (unsigned long long)*(void **)args[0]);
}

可以找到概念证明here