Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Counting Array Elements at Compile Time


Implementation

The design goals for COUNTOF were to:

  • Preserve the good features of the traditional COUNTOF.
  • Present users with a type-safe interface.
  • Generate error messages that at least hint at the problem when an inappropriate argument is passed.

COUNTOF achieves those goals, but the required implementation is a bit obscure (Listing One). In pseudocode, it is simply:

if x is not an array
   issue a compile-time error
else
   use the traditional COUNTOF expression

Probably the best way to understand how COUNTOF does this is to break it into parts. The macro expands to an expression that can be summarized like this:

0 * sizeof(check1) +
0 * sizeof(check2) +
sizeof(x) / sizeof((x)[0])

The first thing to note is that the first two sizeof expressions are not really being used to compute the size of anything. Because they are both multiplied by zero, they do not make any contribution to the result. They are just convenient vehicles for some type checks to verify that the argument is an array.

The check1 expression reinterpret_casts the macro argument x to a pointer. The cast is never actually executed; it is just part of a compile-time check. If the argument is an object of class type, such as an std::vector, then the cast is not legal and the compiler issues an error. The same thing happens if the argument is a floating-point type, a function pointer, or a pointer-to-member. Any pointer to an object would work as the result type, but I chose Bad_arg_to_COUNTOF* (and named the class that way) because the result type is likely to show up in any compiler error messages, and that name gives users a hint at what the problem is.

If the reinterpret_cast succeeds, then x must be an integral or enumerated type, a pointer to an object, or an array. The compiler moves on to the check2 clause. This part of the macro expands approximately to sizeof(check_type(x, &x)), where check_type is an overloaded function [1]. Because this is purely a compile-time computation, the function is never really called or even implemented, but it lets the compiler apply overload resolution to do some further type discrimination. There are three possibilities to consider:

  • x is an integral or enumerated type. In this case, neither of the two overloads is a match, and the compiler issues an error.
  • x is a pointer to an object. If x is a pointer, then the first argument of check_type is a pointer and the second one is a pointer-to-pointer. The best match is the first overload of check_type, the one that returns an Is_pointer. Because Is_pointer is an incomplete type, sizeof(Is_pointer) is not a valid expression, and again the compiler issues an error.
  • x is an array. If x is an array, then the first argument to check_type is an array and the second is a pointer-to-array. A pointer-to-array is not convertible to a pointer-to-pointer, so the first overload of check_type is not a match. However, an array is convertible to a pointer, and a pointer-to-array already is a pointer. Any pointer is convertible to a void*, so the second overload is a match. That overload returns an Is_array, which is a complete type for which sizeof(Is_array) is a valid expression.

At this point, the compiler has excluded every possible type except arrays. The first two lines have reduced to zeros, and the compiler moves on to the third. That line is just sizeof(x)/sizeof((x)[0]); that is, the size in bytes of the entire array divided by the size in bytes of a single element, which yields the number of elements in the array. This is the same expression used by the traditional COUNTOF, and it gives the same result.

Conclusion

The new COUNTOF macro preserves all the benefits of the traditional COUNTOF, while adding the type safety that macros often lack. Its only real disadvantage is the cryptic code needed to implement it. However, because the complexity is hidden behind a simple and well-defined interface that exactly matches the original, it is not a serious problem.

Acknowledgment

Thanks to Andrew Hopkins for his thoughtful review of a draft of this article.

References

  • [1] The method of eliminating pointers with sizeof and a pair of overloaded functions is based on one devised by Jonathan Lundquist. See groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/bd3b7cc6e26d3ebe.
  • [2] The countof and count function templates are adapted from ones by Dietmar Kuehl and Siemel Naran, respectively. See groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/d0faf979c493c19c and groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/8e45b21773e191ea.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.