C++ 为错误使用自定义 print/log 函数添加编译器警告
C++ Add compiler warnings for wrong usage of custom print/log function
我有以下功能,我想像使用 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);
}
如果我忘记为格式字符串中的标记之一添加参数,我想以某种方式收到警告。对于有太多(或错误的参数)的警告也会很棒。
由于忘记了日志记录函数中的参数,我最近遇到了一些崩溃。
如果不能这样做,我该如何重写我的函数,使其具有警告但功能相同?
如果您使用 gcc/g++/clang,您可以使用 format 属性,如 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.
__attribute__
在函数原型之前还是之后都没有关系。
所以在你的情况下你可以这样做:
class LoggingManager {
...
public:
void log(int32_t logLevel, const char *p_str, ...) __attribute__((format (printf, 3, 4)));
...
};
请注意,因为这是一个成员函数,所以您需要考虑传递的隐式 this
参数。所以格式字符串实际上是第三个参数而不是第二个。 (format (printf, 3, 4)
而不是 format (printf, 2, 3)
)
看到效果了 here。
如果你正在使用 Visual Studio 你可以使用 SAL annotation _Printf_format_string_
宏:
#include <sal.h>
void log
(
int32_t log_level
, _In_z_ _Printf_format_string_ const char * psz_format
, ...
);
为了使代码可移植,您可能需要在必要时定义格式属性宏和 SAL 宏替换:
#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);
我有以下功能,我想像使用 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);
}
如果我忘记为格式字符串中的标记之一添加参数,我想以某种方式收到警告。对于有太多(或错误的参数)的警告也会很棒。 由于忘记了日志记录函数中的参数,我最近遇到了一些崩溃。
如果不能这样做,我该如何重写我的函数,使其具有警告但功能相同?
如果您使用 gcc/g++/clang,您可以使用 format 属性,如 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.
__attribute__
在函数原型之前还是之后都没有关系。
所以在你的情况下你可以这样做:
class LoggingManager {
...
public:
void log(int32_t logLevel, const char *p_str, ...) __attribute__((format (printf, 3, 4)));
...
};
请注意,因为这是一个成员函数,所以您需要考虑传递的隐式 this
参数。所以格式字符串实际上是第三个参数而不是第二个。 (format (printf, 3, 4)
而不是 format (printf, 2, 3)
)
看到效果了 here。
如果你正在使用 Visual Studio 你可以使用 SAL annotation _Printf_format_string_
宏:
#include <sal.h>
void log
(
int32_t log_level
, _In_z_ _Printf_format_string_ const char * psz_format
, ...
);
为了使代码可移植,您可能需要在必要时定义格式属性宏和 SAL 宏替换:
#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);