skip to Main Content

As we all know and love (or hate), GCC has the capability of issuing warnings when you attempt to use the printf() family of functions with mismatched type specifications. In our code, we have a number of utility functions of the form:

int zzzprintf(DataType *dt, const char *format, ...) {
    va_list args;
    va_start(args, format);
    int status = vprintf(dt->buf, format, args);
    va_end(args);

    return status
}

What I’d like to see is the same set of warning semantics around the zzzprintf() function, such that if you, say call:

int64_t id64;
zzzprintf(dt, "Hello, %dn", id64);

you get the warning:

/tmp/foo.c: In function ‘main’:
/tmp/foo.c:7:20: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int64_t’ {aka ‘long int’} [-Wformat=]
     zzzprintf("Hello %dn", id64);
                      ~^     ~~
                      %ld

Note: I’m not asking for enhancements to the GCC compiler. I’m looking for some way of telling the compiler to expect this behaviour, through a #pragma or the like. We are currently using gcc-8.4.0, and do not have the capability of easily upgrading our gcc, since we are locked in at Ubuntu 18.04 for the time being.

2

Answers


  1. GCC supports Function Attributes including one to mimic printf-like (or scanf, strftime, or strfmon) checking, format. You can use it by declaring your function like so in the appropriate header:

    int zzzprintf(DataType *dt, const char *format, ...)
            __attribute__ ((format (printf, 2, 3)));
    

    which makes all source files including that header process the arguments using the compiler-checks associated with printf.

    As the link at the top indicates, this has been supported since at least GCC 4.7.2, so your compiler restrictions should not pose an issue.

    Login or Signup to reply.
  2. This is a minor amplification of ShadowRanger‘s answer:

    I sometimes use compilers other than GCC on some of my target platforms, so I use a macro to encapsulate the functionality.

    #if !defined(PRINTFLIKE)
    #if defined(__GNUC__)
    #define PRINTFLIKE(n, m) __attribute__((format(printf, n, m)))
    #else
    #define PRINTFLIKE(n, m) /* If only */
    #endif /* __GNUC__ */
    #endif /* PRINTFLIKE */
    
    …
    
    extern void err_logmsg(FILE *fp, int flags, int estat, const char *format, ...) PRINTFLIKE(4, 5); 
    extern void err_print(int flags, int estat, const char *format, va_list args);
    extern void err_printversion(const char *program, const char *verinfo);
    extern void err_remark(const char *format, ...) PRINTFLIKE(1, 2); 
    extern void err_report(int flags, int estat, const char *format, ...) PRINTFLIKE(3, 4); 
    extern void err_sysrem(const char *format, ...) PRINTFLIKE(1, 2); 
    

    Since I develop with GCC, the error checking works on my development platforms, and I rely on that to protect me on other platforms. Life got easier since I don’t work with any 32-bit platforms any more.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search