这个宏可以转换为函数吗?
在重构代码并摆脱所有那些我们现在被教导讨厌的 #define 的同时,我遇到了用于计算结构中元素数量的美丽:
#define STRUCTSIZE(s) (sizeof(s) / sizeof(*s))
它非常有用,但可以将其转换为内联函数吗或模板?
好吧,ARRAYSIZE 是一个更好的名字,但这是遗留代码(不知道它来自哪里,它至少有 15 年的历史)所以我“按原样”粘贴它。
While refactoring code and ridding myself of all those #defines that we're now taught to hate, I came across this beauty used to calculate the number of elements in a structure:
#define STRUCTSIZE(s) (sizeof(s) / sizeof(*s))
Very useful as it is but can it be converted into an inline function or template?
OK, ARRAYSIZE would be a better name but this is legacy code (no idea where it came from, it's at least 15 years old) so I pasted it 'as is'.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
Windows 特定:
CRT 提供的宏
_countof()
正是用于此目的。MSDN 上的文档链接
Windows specific:
There is the macro
_countof()
supplied by the CRT exactly for this purpose.A link to the doc at MSDN
对于 C99 风格的可变长度数组,纯宏方法 (sizeof(arr) / sizeof(arr[0])) 似乎是唯一可行的方法。
For C99-style variable-length arrays, it appears that the pure macro approach (sizeof(arr) / sizeof(arr[0])) is the only one that will work.
正如 JohnMcG 的回答,但
缺点是您将在二进制文件中为每个类型名、大小组合都有一个副本。
这就是为什么您将其设为内联模板函数。
As JohnMcG's answer, but
Disadvantage is you will have a copy of this in your binary for every Typename, Size combination.
That's why you'd make it an inline template function.
这里详细回答一下:
数组大小确定第 1 部分
和这里:
数组大小确定第 2 部分。
Answered in detail here:
Array Size determination Part 1
and here:
Array Size determination Part 2.
该宏的名称非常具有误导性 - 如果将数组的名称作为宏参数传入,则宏中的表达式将返回数组中的元素数量。
对于其他类型,如果类型是指针,您将得到或多或少毫无意义的东西,否则您将收到语法错误。
通常该宏的名称类似于 NUM_ELEMENTS() 或其他名称以表明其真正的用处。 在 C 中不可能用函数替换宏,但在 C++ 中可以使用模板。
我使用的版本基于 Microsoft 的 winnt.h 标头中的代码(如果发布此代码片段超出了合理使用范围,请告诉我):
另外,Matthew Wilson 的书“Imperfect C++”对这里发生的情况有很好的处理(第 14.3 节) -第211-213页-数组和指针-dimensionof())。
The macro has a very misleading name - the expression in the macro will return the number of elements in an array if an array's name is passed in as the macro parameter.
For other types you'll get something more or less meaningless if the type is a pointer or you'll get a syntax error.
Usually that macro is named something like NUM_ELEMENTS() or something to indicate its true usefulness. It's not possible to replace the macro with a function in C, but in C++ a template can be used.
The version I use is based on code in Microsoft's winnt.h header (please let me know if posting this snippet goes beyond fair use):
Also, Matthew Wilson's book "Imperfect C++" has a nice treatment of what's going on here (Section 14.3 - page 211-213 - Arrays and Pointers - dimensionof()).
是的,它可以在 C++ 中制作一个模板
来使用:
请参阅上面或下面的 BCS 帖子,了解使用一些轻型模板元编程在编译时对类执行此操作的很酷的方法。
Yes it can be made a template in C++
to use:
See BCS's post above or below about a cool way to do this with a class at compile time using some light template metaprogramming.
xtofl 具有查找数组大小的正确答案。 不需要宏或模板来查找结构的大小,因为 sizeof() 应该做得很好。
我同意预处理器是邪恶的,但是有它是最不邪恶的替代方案的场合。
xtofl has the right answer for finding an array size. No macro or template should be necessary for finding the size of a struct, since sizeof() should do nicely.
I agree the preprocessor is evil, but there are occasions where it is the least evil of the alternatives.
我认为这并不能真正计算出结构中元素的数量。 如果结构被压缩并且您使用了小于指针大小的东西(例如 32 位系统上的 char),那么您的结果是错误的。 另外,如果该结构包含一个结构,那么你也错了!
I don't think that that really does work out the number of elements in a structure. If the structure is packed and you used things smaller than the pointer size (such as char on a 32-bit system) then your results are wrong. Also, if the struct contains a struct you are wrong too!
我更喜欢 [BCS] 建议的枚举方法(在 可以吗宏可以转换为函数吗?)
这是因为您可以在编译器期望编译时间常量的地方使用它。 该语言的当前版本不允许您使用函数结果作为编译时常量,但我相信这将出现在编译器的下一个版本中:
此方法的问题在于,与类一起使用时它不会生成编译时错误已重载“*”运算符(有关详细信息,请参阅下面的代码)。
不幸的是,“BCS”提供的版本没有完全按预期编译,所以这是我的版本:
I prefer the enum method suggested by [BCS](in Can this macro be converted to a function?)
This is because you can use it where the compiler is expecting a compile time constant. The current version of the language does not let you use functions results for compile time consts but I believe this coming in the next version of the compiler:
The problem with this method is that it does not generate a compile time error when used with a class that has overloaded the '*' operator (see code below for details).
Unfortunately the version supplied by 'BCS' does not quite compile as expected so here is my version:
简化@KTC,因为我们在模板参数中有数组的大小:
缺点是您将在二进制文件中为每个类型名、大小组合都有一个副本。
Simplfying @KTC's, since we have the size of the array in the template argument:
Disadvantage is you will have a copy of this in your binary for every Typename, Size combination.
编辑:从 Doug 的代码中
我被告知第二个不起作用。 OTOH 类似的东西应该是可行的,我只是没有使用足够的 C++ 来修复它。
有关编译时内容的 C++(和 D)模板页面
Edit: From Doug's code
I've been told that the 2nd one doesn't work. OTOH something like it should be workable, I just don't use C++ enough to fix it.
A page on C++ (and D) templates for compile time stuff
你的宏命名错误,它应该被称为 ARRAYSIZE。 它用于确定大小在编译时固定的数组中元素的数量。 这是一种可行的方法:
使用起来有点脆弱,因为你可能会犯这个错误:
您现在将迭代 i = 0 到 < 1、然后把头发扯下来。
Your macro is misnamed, it should be called ARRAYSIZE. It is used to determine the number of elements in an array whos size is fixed at compile time. Here's a way it can work:
It's kind of brittle to use, because you can make this mistake:
You will now iterate for i = 0 to < 1 and tear your hair out.
与模板类不同,模板函数的类型是自动推断的。 您可以更简单地使用它:
但我确实同意它不适用于结构:它适用于数组。 所以我宁愿称之为 Arraysize :)
The type of a template function is inferred automatically, in contrast with that of a template class. You can use it even simpler:
But I do agree it doesn't work for structs: it works for arrays. So I would rather call it Arraysize :)
KTC 的解决方案很干净,但它不能在编译时使用,并且依赖于编译器优化来防止代码膨胀和函数调用开销。
可以使用仅编译时元函数计算数组大小,运行时成本为零。 BCS 走在正确的轨道上,但该解决方案是不正确的。
这是我的解决方案:
使用测试代码(使用 Boost.StaticAssert 来演示仅编译时的用法):
此解决方案在编译时拒绝非数组类型,因此它不会像宏版本那样被指针混淆。
KTC's solution is clean but it can't be used at compile-time and it is dependent on compiler optimization to prevent code-bloat and function call overhead.
One can calculate array size with a compile-time-only metafunction with zero runtime cost. BCS was on the right track but that solution is incorrect.
Here's my solution:
with test code (using Boost.StaticAssert to demonstrate compile-time-only usage):
This solution rejects non-array types at compile time so it will not get confused by pointers as the macro version does.
到目前为止,还没有人提出一种可移植的方法来获取数组的大小,当您只有数组的实例而不是其类型时。 ( typeof 和 _countof 不可移植,因此无法使用。)
我会按以下方式执行此操作:
我将构造包装到宏中以获得一些不错的语法。 如果您想摆脱它,唯一的选择就是手动进行替换。
None has so far proposed a portable way to get the size of an array when you only have an instance of an array and not its type. (typeof and _countof is not portable so can't be used.)
I'd do it the following way:
I wrapped the construct into a macro to have some decent syntax. If you want to get rid of it your only option is to do the substitution manually.
如前所述,代码实际上计算的是数组中的元素数量,而不是结构体中的元素数量。 当我需要时,我会明确地写出 sizeof() 除法。 如果我要把它变成一个函数,我想在它的定义中清楚地表明它需要一个数组。
上面的内容与 xtofl 的类似,只是它保护反对传递一个指向它的指针(即指向一个动态分配的数组)并错误地得到错误的答案。
编辑:按照JohnMcG进行简化。
编辑:内联。
不幸的是,上面没有提供编译时答案(即使编译器在后台将其内联并优化为常量),因此不能用作编译时常量表达式。 即它不能用作声明静态数组的大小。 在 C++0x 下,如果将关键字 inline 替换为 constexpr(constexpr 是隐式内联的),这个问题就会消失。
jwfearn 的解决方案适用于编译时,但涉及typedef 有效地“保存”了新名称声明中的数组大小。 然后通过使用该新名称初始化常量来计算数组大小。 在这种情况下,我们也可以从一开始就将数组大小简单地保存为一个常量。
Martin York 发布的解决方案也可以在编译时运行,但涉及使用非标准 typeof() 运算符。 解决这个问题的方法是等待 C++0x 并使用 decltype(此时实际上不需要它来解决这个问题,因为我们将拥有 constexpr) )。 另一种替代方法是使用 Boost.Typeof,在这种情况下,我们最终会使用
and 来编写
其中 foo 是数组的名称。
As been stated, the code actually work out the number of elements in an array, not struct. I would just write out the sizeof() division explicitly when I want it. If I were to make it a function, I would want to make it clear in its definition that it's expecting an array.
The above is similar to xtofl's, except it guards against passing a pointer to it (that says point to a dynamically allocated array) and getting the wrong answer by mistake.
EDIT: Simplified as per JohnMcG.
EDIT: inline.
Unfortunately, the above does not provide a compile time answer (even if the compiler does inline & optimize it to be a constant under the hood), so cannot be used as a compile time constant expression. i.e. It cannot be used as size to declare a static array. Under C++0x, this problem go away if one replaces the keyword inline by constexpr (constexpr is inline implicitly).
jwfearn's solution work for compile time, but involve having a typedef which effectively "saved" the array size in the declaration of a new name. The array size is then worked out by initialising a constant via that new name. In such case, one may as well simply save the array size into a constant from the start.
Martin York's posted solution also work under compile time, but involve using the non-standard typeof() operator. The work around to that is either wait for C++0x and use decltype (by which time one wouldn't actually need it for this problem as we'll have constexpr). Another alternative is to use Boost.Typeof, in which case we'll end up with
and is used by writing
where foo is the name of an array.