如何将static_assert与sizeof和stringify结合起来?

Posted

技术标签:

【中文标题】如何将static_assert与sizeof和stringify结合起来?【英文标题】:How to combine static_assert with sizeof and stringify? 【发布时间】:2012-07-16 14:41:30 【问题描述】:

内存使用在我的应用程序中非常重要。因此,我有特定的断言在编译时检查内存大小,如果大小与我们之前认为正确的大小不同,则给出 static_assert。

我已经定义了一个这样的宏:

#define CHECKMEM(mytype, size) static_assert((sizeof(objectType) == size)), "Size incorrect for " #mytype "!");

这个宏可以很容易地写出来:

CHECKMEM(Book,144);
CHECKMEM(Library,80);

问题是,当这个 static_assert 关闭时,可能很难找出新的大小应该是多少(例如,通过使用隐藏的编译器选项“/d1 reportAllClassLayout”)。 如果我可以包含实际尺寸会更方便,所以不要:

书的尺寸不正确!

它会显示

书的尺寸不正确! (预期为 144,大小为 152)

我试着写这样的东西:

#define CHECKMEM(mytype, size) static_assert((sizeof(objectType) == size)), "Size incorrect for " #mytype "! (expected" #size ", size is " #sizeof(mytype) ")");

但你不能在函数调用中使用字符串化 (#) 运算符。

我还尝试添加双字符串化技巧,如下所示:

#define STR1(x) #x 
#define STR2(x) STR1(x) 
#define CHECKMEM(mytype, size) static_assert((sizeof(objectType) == size)), "Size incorrect for " #mytype "! (expected" #size ", size is " STR2(sizeof(mytype)) ")");

但不是打印size is 152,而是打印size is sizeof(Book)

有没有办法在 static_assert 中对 sizeof 的结果进行字符串化?

【问题讨论】:

请注意,sizeof 不是函数调用 问题是static_assert 的第二个参数必须是字符串文字,并且你不能在预处理器中构建它,因为你不能在那里使用 sizeof。 【参考方案1】:

一个简单实用的解决方案是使用2个static_assert-s:

#define CHECKMEM(mytype, size) \
static_assert( sizeof(mytype) <= (size), "Size of " #mytype " is greater than " #size "!" ); \
static_assert( sizeof(mytype) >= (size), "Size of " #mytype " is less than "    #size "!" )

【讨论】:

【参考方案2】:

如果您可以稍微修改结构的定义并且不介意一些丑陋,这里是一个替代的仅标头解决方案。

template <int RealSize = 0, int ExpectedSize = 0>
struct _MyStruct 
    static_assert(RealSize == ExpectedSize, "size is invalid");

    int x;
;

typedef _MyStruct<sizeof(_MyStruct<>), 4> MyStruct;

clang 输出:

prog.cpp:4:5: error: static_assert failed "size is invalid"
    static_assert(RealSize == ExpectedSize, "size is invalid");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~
prog.cpp:12:14: note: in instantiation of template class '_MyStruct<4, 8>' requested here
    MyStruct m;

这里需要注意的是,只有在某处实例化类型时才会进行检查——仅使用指针不会触发错误,因此绝对不适合所有情况!

【讨论】:

【参考方案3】:

我会在函数模板上使用调度来进行检查:

#include <cstddef>

template <typename ToCheck, std::size_t ExpectedSize, std::size_t RealSize = sizeof(ToCheck)>
void check_size() 
  static_assert(ExpectedSize == RealSize, "Size is off!");


struct foo

  char bla[16];
;

int main()

  check_size<foo, 8>();
  return 0;

结果:

In instantiation of ‘void check_size() [with ToCheck = foo; long unsigned int ExpectedSize = 8ul; long unsigned int RealSize = 16ul]’:
bla.cpp:15:22:   required from here
bla.cpp:5:1: error: static assertion failed: Size is off!

调试信息在back-trace的模板参数中。

如果这真的更好,您将不得不做出决定,这也取决于编译器。它还使您能够使用模板图隐藏预期的大小,总结为最大大小和其他花哨的东西。

【讨论】:

【参考方案4】:

正如你所发现的,问题就在这里(另见very similar question):

#define CHECKMEM(mytype, size)  #sizeof(mytype)

这是不可能的,因为字符串化是由预处理器完成的,并且在编译期间会评估 sizeof。

【讨论】:

【参考方案5】:

根据您的编译器,模板可能会有所帮助:

template<int s, int t> struct check_size 
  static_assert(s == t, "wrong size");
;
check_size<2+2, 5> doubleplusungood;

gcc 输出:

prog.cpp: In instantiation of 'check_size<4, 5>':
prog.cpp:5:20:   instantiated from here
prog.cpp:2:3: error: static assertion failed: "wrong size"

【讨论】:

加一个非常有创意的例子,很容易理解

以上是关于如何将static_assert与sizeof和stringify结合起来?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 sizeof(my_arr)[0] 编译并等于 sizeof(my_arr[0])?

c#/c++ 如何将结构的结构与 marshal.sizeof 联合

sizeof 与 strlen 的使用例子

wstring如何转换成 LPCTSTR

C++11 static_assert关键字

C++11 static_assert关键字