Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid warning in wrapper around printf

I have an error reporting functionality in my little C library I'm writing. I want to provide an errorf function in addition to the plain error function to allow embedding information in error messages easily.

/*
 * Prints a formatted error message. Use it as you would use 'printf'. See the
 * 'sio_error' function.
 */
void sio_errorf(const char *format, ...) {
    // Print the error prefix                           
    if (g_input == STDIN) fputs("error: ", stderr);
    else fprintf(stderr, "%s: ", g_progname);

    // Pass on the varargs on to 'vfprintf'. 
    va_list arglist;
    va_start(arglist, format);
    // This may produce the following warning -- ignore it:
    //     warning: format string is not a string literal
    vfprintf(stderr, format, arglist);
    va_end(arglist);
    fputc('\n', stderr);
}

The problem is, I get this warning (compiling with clang 4.0 with the -Weverything switch):

warning: format string is not a string literal

I understand why doing this would be bad. Is there any way I can get rid of this warning? Can I somehow enforce that the format argument sio_errorf be a string literal, so that the compiler knows that it always will be, and that I'm simply passing it on?

I know I can use -Wno-format-nonliteral, but only if other people are going to manually compile it too, they won't do that. I'd rather something in the source code that silences the warning.

Ideally I would still get the warning if the string I passed to sio_errorf actually isn't a literal, but I'm not sure if that's possible.

like image 277
mk12 Avatar asked Aug 29 '12 04:08

mk12


2 Answers

If you're using GCC or one of its relatives, try an attribute on the declaration:

void sio_errorf(const char *format, ...) __attribute__((format(printf, 1, 2)));

To add the attribute to a definition, you can use this:

__attribute__((format(printf, 1, 2)))
    static void sio_errorf(const char *format, ...) {
      ....
like image 125
justin Avatar answered Sep 29 '22 16:09

justin


Many compilers will allow you to set warning levels in one way or another. For example, gcc allows the control via -W flags on the command line when invoking the compiler.

Hopefully that's the compiler you're using since a program like this:

#include <stdio.h>
int main (void) {
    char *s = "xyzzy\n";
    printf (s);
    return 0;
}

generates the exact message you describe (assuming you've enabled the warnings with -Wformat and -Wformat-nonliteral).

The particular command line argument you would be looking for is:

-Wno-format-nonliteral

which will prevent complaints about the use of non-literal strings in those functions.

However, you may be looking for something more fine-grained so it also allows you to specify the disposition of certain diagnostic messages on the fly within your code with pragmas:

#include <stdio.h>
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
int main (void) {
    char *s = "xyzzy\n";
    printf (s);
    return 0;
}
#pragma GCC diagnostic warning "-Wformat-nonliteral"

If you compile that with -Wformat -Wformat-nonliteral, you won't see the warning, because you've told gcc to ignore that particular warning for the main function.

Later versions of gcc than the one I run have the following options:

#pragma GCC diagnostic push
#pragma GCC diagnostic pop

which will push and pop the state of the diagnostics. This gets around the problems in my code above where you might configure that warning as an error - my second pragma would change it into a warning.

The use of push/pop would allow restoration to its original disposition with something like:

#include <stdio.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
int main (void) {
    char *s = "xyzzy\n";
    printf (s);
    return 0;
}
#pragma GCC diagnostic pop
like image 44
paxdiablo Avatar answered Sep 29 '22 16:09

paxdiablo