4

I've got the following function and I'd like to have warnings as one has when using printf:

void LoggingManager::log(int32_t logLevel, const char *p_str, ...)
{
    va_list args;
    va_start(args, p_str);
    vsnprintf(s_LogginManagerBuffer, LOGGING_MANAGER_BUFFER_SIZE - 1, p_str, args);
    va_end(args);

    internalLog(s_LogginManagerBuffer);
}

I'd like to somehow have warnings if I forget to add an argument for one of the tokens in the format-string. Also warnings for having too many (or wrong arguments) would be awesome. I recently faced some crashes due to forgetting an argument in the logging function.

If it's not possible to do it this way, how could I rewrite my function, to have warnings but the same functionality?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
keyboard
  • 2,137
  • 1
  • 20
  • 32

2 Answers2

6

If you're using gcc/g++/clang you can use the format attribute as specified on this page:

format (archetype, string-index, first-to-check)

The format attribute specifies that a function takes printf, scanf, strftime or strfmon style arguments that should be type-checked against a format string. For example, the declaration:

extern int my_printf (void *my_object, const char *my_format, ...) __attribute__ ((format (printf, 2, 3)));

causes the compiler to check the arguments in calls to my_printf for consistency with the printf style format string argument my_format.

It doesn't matter whether the __attribute__ is before or after the function prototype.

So in your case you can do something like this:

class LoggingManager {
    ...
public:
    void log(int32_t logLevel, const char *p_str, ...) __attribute__((format (printf, 3, 4)));
    ...
};

Note that because this is a member function you need to account for the implicit this parameter passed. So the format string would actually be the 3rd argument instead of the 2nd. (format (printf, 3, 4) instead of format (printf, 2, 3))

See it work here.

Kevin
  • 6,993
  • 1
  • 15
  • 24
0

If you are using Visual Studio you can use SAL annotation _Printf_format_string_ macro:

#include <sal.h>

void log
(
    int32_t                                    log_level
,   _In_z_ _Printf_format_string_ const char * psz_format
,   ...
);

To make code portable you may want to define format attribute macro and SAL macros substitutions when necessary:

#if defined(__GNUC__)
#define ATTRIBUTE_PRINTF(format_index, vargs_index) __attribute__((__format__ (__printf__, format_index, vargs_index)))
#else
#define ATTRIBUTE_PRINTF(format_index, vargs_index)
#endif

#if defined(_MSC_VER)
#include <sal.h>
#else
#define _In_z_
#define _Printf_format_string_
#endif

void log
(
    int32_t                                    log_level
,   _In_z_ _Printf_format_string_ const char * psz_format
,   ...
) ATTRIBUTE_PRINTF(2, 3);
user7860670
  • 35,849
  • 4
  • 58
  • 84
  • doesn't work for me. If I declare my own function with _Printf_format_string_, I get no warnings at all, if I use standard sprintf, everything is ok. VS2017 15.9.7, included. any ideas? – George Hazan Feb 22 '19 at 10:44
  • @GeorgeHazan Perhaps warnings are suppressed or C++ Code Analysis is not executed. – user7860670 Feb 22 '19 at 11:31
  • when I replace my own function with printf without changing anything else, warnings are present, so it's not the problem, I've summarized the problem in a short snippet: https://stackoverflow.com/questions/54826028/why-printf-format-string-macro-doesnt-produce-any-warnings – George Hazan Feb 22 '19 at 15:31