C 这可以自定义printf吗?

C 这可以自定义printf吗?,c,linux,customization,glibc,C,Linux,Customization,Glibc,我有一些需要经常打印的结构。现在,我在这个结构周围使用一个经典的打印包装器: void printf_mystruct(struct* my_struct) { if (my_struct==NULL) return; printf("[value1:%d value2:%d]", struct->value1, struct->value2); } 这个功能很方便,但也很有限。我无法在不制作新包装器的情况下预先编写或附加一些文本。我知道我可以使用va_argfamil

我有一些需要经常打印的结构。现在,我在这个结构周围使用一个经典的打印包装器:

void printf_mystruct(struct* my_struct)
{
   if (my_struct==NULL) return;
   printf("[value1:%d value2:%d]", struct->value1, struct->value2);
}
这个功能很方便,但也很有限。我无法在不制作新包装器的情况下预先编写或附加一些文本。我知道我可以使用va_argfamily来预处理或预处理一些文本,但我觉得我将重新实现控制盘

我想知道是否可以为printf编写一个自定义函数。我希望能够写出这样的东西:

register2printf("%mys", &printf_mystruct); 
...
if (incorrect)
  printf("[%l] Struct is incorrect : %mys\n", log_level, my_struct);
这可能吗?我该怎么做


注意:我在Ubuntu Linux 10.04下使用gcc。

这在标准C中是不可能的。您不能扩展
printf
来添加自定义格式字符串。您的助手函数方法可能与您在C的约束条件下得到的方法一样好。

不幸的是,这是不可能的


最简单的解决方案可能是采用小型的printf(例如,从嵌入式系统的libc)实现并扩展它以满足您的需要。

不,这是不可能的。另一种方法是围绕
printf()
本身制作自己的包装器。它将解析格式字符串并像
printf()
那样处理转换。如果转换是您的自定义转换之一,它将打印您需要的任何内容,如果不是,它将调用系统的
*printf()
函数之一,让它为您执行转换

请注意,这是一项非常重要的任务,您必须小心地像解析
printf()
一样解析格式字符串。请参见
man 3 printf
。您可以使用
中的函数读取变量参数列表

一旦有了这样一个包装器,就可以通过使用函数指针使其可扩展(自定义转换不必硬编码到包装器中)。

您可以使用该函数获得结构的字符串表示形式:

char* repr_mystruct(char* buffer, struct* my_struct)
{
    sprintf(buffer, "[string:%s value1:%d value2:%d]", struct->value1, struct->value2);
    return buffer;
}
然后将数据打印到输出流中

char buffer[512]; //However large you need it to be
printf("My struct is: %s", repr_mystruct(buffer, &my_struct))
编辑:修改函数以允许传递缓冲区(请参阅下面的讨论)

注2:格式字符串需要三个参数,但在示例中只传递了两个参数。

很抱歉,在使用Glibc的Linux上,有些答案是不正确的

在使用GNU Glibc的Linux上,您可以:调用
register\u printf\u函数
来定义
printf
格式字符串中
%Y
的含义

然而,这种行为是Glibc特有的,甚至可能会过时。。。我不确定我会推荐这种方法

如果C++中的编码,C++流库有可以扩展的操作器,也可以为类型加载,<代码>操作符> P>,请将其放在这里:

printf("%s: pid = %lu, ppid = %lu, pgrp = %lu, tpgrp = %lu\n", name, 
        (unsigned long int)getpid(), (unsigned long int)getppid(), (unsigned long int)getpgrp(), 
        (unsigned long int)tcgetpgrp(STDIN_FILENO));

假设您想要可移植的代码,glibc扩展已经过时了。但即使遵守C99和POSIX标准,这也是非常可能的,我只是写了一个

您不必重新实现printf,不幸的是,您需要使代码足够智能,以便解析printf格式字符串,并从中推断可变参数的C类型

在堆栈上放置可变参数时,不包括类型或大小信息

void my_variadic_func(fmt, ...)
{

}

my_variadic_func("%i %s %i", 1, "2", 3);
在上面的64位系统示例中,使用48位寻址,编译器可能最终会分配4bytes+6bytes+4byte=14字节的堆栈内存,并将值打包到其中。我说可能,因为内存分配和参数打包的方式是特定于实现的

这意味着,为了访问上述字符串中
%s
的指针值,您需要知道第一个参数的类型是
int
,因此您可以将变量列表光标提前到正确的点

获取该类型信息的唯一方法是查看格式字符串,并查看用户指定的类型(在本例中为
%i

因此,为了实现@AmbrozBizjak的建议,即将subfmt字符串传递给printf,您需要解析fmt字符串,并且在每个完整的非自定义fmt说明符之后,将一个va_列表推进fmt类型(无论多大字节宽)

当你点击一个自定义的fmt说明符时,你的va_列表就在解包参数的正确位置。然后,可以使用
va_arg()
获取自定义参数(传递正确的类型),并使用它运行需要的任何代码,以生成自定义fmt说明符的输出

将上一次printf调用的输出与自定义fmt说明符的输出连接起来,并继续处理,直到结束,此时再次调用printf以处理其余的格式字符串

代码更复杂(所以我在下面包括了它),但这让您对必须做的事情有了一个基本的了解

我的代码也使用talloc。。。但您可以使用标准内存函数来实现,只需要更多的字符串争用

char *custom_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap)
{
    char const  *p = fmt, *end = p + strlen(fmt), *fmt_p = p, *fmt_q = p;
    char        *out = NULL, *out_tmp;
    va_list     ap_p, ap_q;

    out = talloc_strdup(ctx, "");
    va_copy(ap_p, ap);
    va_copy(ap_q, ap_p);

    do {

        char        *q;
        char        *custom;
        char        len[2] = { '\0', '\0' };
        long        width = 0, group = 0, precision = 0, tmp;

        if ((*p != '%') || (*++p == '%')) {
            fmt_q = p + 1;
            continue;   /* literal char */
        }

        /*
         *  Check for parameter field
         */
        tmp = strtoul(p, &q, 10);
        if ((q != p) && (*q == '$')) {
            group = tmp;
            p = q + 1;
        }

        /*
         *  Check for flags
         */
        do {
            switch (*p) {
            case '-':
                continue;

            case '+':
                continue;

            case ' ':
                continue;

            case '0':
                continue;

            case '#':
                continue;

            default:
                goto done_flags;
            }
        } while (++p < end);
    done_flags:

        /*
         *  Check for width field
         */
        if (*p == '*') {
            width = va_arg(ap_q, int);
            p++;
        } else {
            width = strtoul(p, &q, 10);
            p = q;
        }

        /*
         *  Check for precision field
         */
        if (*p == '.') {
            p++;
            precision = strtoul(p, &q, 10);
            p = q;
        }

        /*
         *  Length modifiers
         */
        switch (*p) {
        case 'h':
        case 'l':
            len[0] = *p++;
            if ((*p == 'h') || (*p == 'l')) len[1] = *p++;
            break;

        case 'L':
        case 'z':
        case 'j':
        case 't':
            len[0] = *p++;
            break;
        }

        /*
         *  Types
         */
        switch (*p) {
        case 'i':                               /* int */
        case 'd':                               /* int */
        case 'u':                               /* unsigned int */
        case 'x':                               /* unsigned int */
        case 'X':                               /* unsigned int */
        case 'o':                               /* unsigned int */
            switch (len[0]) {
            case 'h':
                if (len[1] == 'h') {                    /* char (promoted to int) */
                    (void) va_arg(ap_q, int);
                } else {
                    (void) va_arg(ap_q, int);           /* short (promoted to int) */
                }
                break;

            case 'L':
                if ((*p == 'i') || (*p == 'd')) {
                    if (len [1] == 'L') {
                        (void) va_arg(ap_q, long);      /* long */
                    } else {
                        (void) va_arg(ap_q, long long);     /* long long */
                    }
                } else {
                    if (len [1] == 'L') {
                        (void) va_arg(ap_q, unsigned long); /* unsigned long */
                    } else {
                        (void) va_arg(ap_q, unsigned long long);/* unsigned long long */
                    }
                }
                break;

            case 'z':
                (void) va_arg(ap_q, size_t);                /* size_t */
                break;

            case 'j':
                (void) va_arg(ap_q, intmax_t);              /* intmax_t */
                break;

            case 't':
                (void) va_arg(ap_q, ptrdiff_t);             /* ptrdiff_t */
                break;

            case '\0':  /* no length modifier */
                if ((*p == 'i') || (*p == 'd')) {
                    (void) va_arg(ap_q, int);           /* int */
                } else {
                    (void) va_arg(ap_q, unsigned int);      /* unsigned int */
                }
            }
            break;

        case 'f':                               /* double */
        case 'F':                               /* double */
        case 'e':                               /* double */
        case 'E':                               /* double */
        case 'g':                               /* double */
        case 'G':                               /* double */
        case 'a':                               /* double */
        case 'A':                               /* double */
            switch (len[0]) {
            case 'L':
                (void) va_arg(ap_q, long double);           /* long double */
                break;

            case 'l':   /* does nothing */
            default:    /* no length modifier */
                (void) va_arg(ap_q, double);                /* double */
            }
            break;

        case 's':
            (void) va_arg(ap_q, char *);                    /* char * */
            break;

        case 'c':
            (void) va_arg(ap_q, int);                   /* char (promoted to int) */
            break;

        case 'p':
            (void) va_arg(ap_q, void *);                    /* void * */
            break;

        case 'n':
            (void) va_arg(ap_q, int *);                 /* int * */
            break;

        /*
         *  Custom types
         */
        case 'v':
        {
            value_box_t const *value = va_arg(ap_q, value_box_t const *);

            /*
             *  Allocations that are not part of the output
             *  string need to occur in the NULL ctx so we don't fragment
             *  any pool associated with it.
             */
            custom = value_box_asprint(NULL, value->type, value->datum.enumv, value, '"');
            if (!custom) {
                talloc_free(out);
                return NULL;
            }

        do_splice:
            /*
             *  Pass part of a format string to printf
             */
            if (fmt_q != fmt_p) {
                char *sub_fmt;

                sub_fmt = talloc_strndup(NULL, fmt_p, fmt_q - fmt_p);
                out_tmp = talloc_vasprintf_append_buffer(out, sub_fmt, ap_p);
                talloc_free(sub_fmt);
                if (!out_tmp) {
                oom:
                    fr_strerror_printf("Out of memory");
                    talloc_free(out);
                    talloc_free(custom);
                    va_end(ap_p);
                    va_end(ap_q);
                    return NULL;
                }
                out = out_tmp;

                out_tmp = talloc_strdup_append_buffer(out, custom);
                TALLOC_FREE(custom);
                if (!out_tmp) goto oom;
                out = out_tmp;

                va_end(ap_p);       /* one time use only */
                va_copy(ap_p, ap_q);    /* already advanced to the next argument */
            }

            fmt_p = p + 1;
        }
            break;

        case 'b':
        {
            uint8_t const *bin = va_arg(ap_q, uint8_t *);

            /*
             *  Only automagically figure out the length
             *  if it's not specified.
             *
             *  This allows %b to be used with stack buffers,
             *  so long as the length is specified in the format string.
             */
            if (precision == 0) precision = talloc_array_length(bin);

            custom = talloc_array(NULL, char, (precision * 2) + 1);
            if (!custom) goto oom;
            fr_bin2hex(custom, bin, precision);

            goto do_splice;
        }

        default:
            break;
        }
        fmt_q = p + 1;
    } while (++p < end);

    /*
     *  Print out the rest of the format string.
     */
    if (*fmt_p) {
        out_tmp = talloc_vasprintf_append_buffer(out, fmt_p, ap_p);
        if (!out_tmp) goto oom;
        out = out_tmp;
    }

    va_end(ap_p);
    va_end(ap_q);

    return out;
}
char*custom\u vasprintf(TALLOC\u CTX*CTX,char const*fmt,va\u list ap)
{
字符常量*p=fmt,*end=p+strlen(fmt),*fmt_p=p,*fmt_q=p;
char*out=NULL,*out\u tmp;
va_列表ap_p,ap_q;
out=talloc_标准值(ctx,“”);
副本(ap、ap);
va_副本(ap_q,ap_p);
做{
char*q;
习惯;
字符长度[2]={'\0','\0'};
长宽=0,组=0,精度=0,tmp;
如果((*p!='%')| |(*++p=='%')){
fmt_q=p+1;
继续;/*文字字符*/
}
/*
*检查参数字段
*/
tmp=strtoul(p&q,10);
如果((q!=p)&(*q=='$')){
组=tmp;
p=q+1;
}
/*
*检查旗帜
*/
做{
开关(*p){
案例'-':
继续;
格“+”:
继续;
案例“”:
继续;
案例