GCC和Clang都支持对变量参数函数(如printf
进行编译时检查。 这些编译器接受如下语法:
extern void dprintf(int dlevel, const char *format, ...) __attribute__((format(printf, 2, 3))); /* 2=format 3=params */
在OSX上,Cocoa框架也对NSString
使用了这个扩展:
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
在我们公司,我们有一个自定义C ++框架,其中包含一些类,如BaseString
所有类都派生自BaseObject
。 在BaseString
有一些类似于sprintf
变量参数方法,但有一些扩展。 例如, "%S"
需要类型为BaseString*
的参数,而"%@"
需要BaseObject*
参数。
我想对我们项目中的参数执行编译时检查,但由于扩展, __attribute__((format(printf)))
给出很多误报警告。
有没有办法为两个编译器之一定制__attribute__((format))
的支持? 如果这需要编译器源的补丁,它是否可以在合理的时间内完成? 或者,是否有其他类似工具的lint可以执行检查?
使用最新版本的GCC(我建议使用4.7或更新版本,但您可以尝试使用GCC 4.6),您可以通过GCC插件(使用PLUGIN_ATTRIBUTES
挂钩)或MELT扩展添加自己的变量和函数属性。 MELT是一种扩展GCC的域特定语言(作为[meta-]插件实现)。
如果使用插件(例如MELT),则无需重新编译GCC的源代码。 但是你需要一个支持插件的GCC(用gcc -v
检查)。
一些Linux发行版不能在他们的gcc
启用插件 – 请向您的发行商投诉; 其他人提供了GCC插件开发包,例如Debian或Ubuntu的gcc-4.7-plugin-dev
。
这是可行的,但这当然不容易; 部分问题是BaseString
和BaseObject
是用户定义的类型,因此您需要动态定义格式说明符。 幸运的是,gcc至少支持这一点,但仍需要修补编译器。
神奇的是gcc/c-family/c-format.c
中的handle_format_attribute
函数,它调用引用用户定义类型的格式说明符的初始化函数。 支持的一个很好的例子是gcc_gfc
格式类型,因为它为locus *
定义了一个格式说明符%L
:
/* This will require a "locus" at runtime. */ { "L", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "R", NULL },
显然,虽然您希望将print_char_table
数组基于print_char_table
,因为它定义了标准的printf
说明符; gcc_gfc
大幅减少。
添加gcc_gfc
的补丁是https://gcc.gnu.org/ml/fortran/2005-07/msg00018.html ; 从那个补丁中应该很明显你需要如何以及在哪里进行添加。
在问了这个问题一年半之后,我提出了一种完全不同的方法来解决真正的问题: 有没有办法静态检查自定义可变参数格式化语句的类型?
为了完整性,因为它可以帮助其他人,这是我最终实现的解决方案。 它比原始问题有两个优点:
Perl脚本解析源代码,查找格式化字符串并解码其中的百分比修饰符。 然后它通过调用模板标识函数CheckFormat<>
来包装所有参数。 例:
str->appendFormat("%hhu items (%.2f %%) from %S processed", nbItems, nbItems * 100. / totalItems, subject);
变为:
str->appendFormat("%hhu items (%.2f %%) from %S processed", CheckFormat(nbItems ), CheckFormat(nbItems * 100. / totalItems ), CheckFormat(subject ));
枚举CFL
, CFM
和模板函数CheckFormat
必须在这样的公共头文件中定义(这是一个提取,大约有24个重载)。
enum class CFL { c, d, i=d, star=i, u, o=u, x=u, X=u, f, F=f, e=f, E=f, g=f, G=f, p, s, S, P=S, at }; enum class CFM { hh, h, l, z, ll, L=ll, _ }; template inline T CheckFormat(T value) { CFL test= value; (void)test; return value; } template<> inline const BaseString* CheckFormat(const BaseString* value) { return value; } template<> inline const BaseObject* CheckFormat(const BaseObject* value) { return value; } template<> inline const char* CheckFormat(const char* value) { return value; } template<> inline const void* CheckFormat(const void* value) { return value; } template<> inline char CheckFormat(char value) { return value; } template<> inline double CheckFormat(double value) { return value; } template<> inline float CheckFormat(float value) { return value; } template<> inline int CheckFormat(int value) { return value; } ...
在出现编译错误之后,很容易恢复原始表单,并将正则表达式CheckFormat<[^<]*>((.*?) )
替换为其捕获。
使用c ++ 11,可以通过使用constexpr
, decltype
和variadic参数包的巧妙组合替换__attribute__ ((format))
来解决此问题。 将格式字符串传递给constexpr
函数,该函数在编译时提取所有%
说明符,并validation第n个说明符是否与(n + 1)’st参数的decltype
匹配。
以上就是c/c++开发分享自定义支持__attribute __((格式))相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/545913.html