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


Arrays as Function Arguments

Suppose you wanted to declare a function that takes an array of a specific size as its argument. For example, you might want a function to compute the distance of a point from the origin. A first attempt might look something like this:


double dist(const double x[3]);


Unfortunately, the compiler discards the size you supplied and interprets the function as one taking a const double* as its argument, so that calling it with any size of array is syntactically legal (but probably gives unexpected results at runtime). If you really want the compiler to enforce that the function receives an array of exactly three elements, you can use an array reference (as shown below). Then you can write and call the function just as you would expect:


double dist(const double(&x)[3])
{
  return sqrt( x[0] * x[0] +
               x[1] * x[1] +
               x[2] * x[2] );
}
double a[] = { 3, 4, 5 };
double d = dist(a);  // OK
double b[] = { 3, 4, 5, 6 };
double d2 = dist(b);  // error

—I.J.J.

countof Without Macros

The COUNTOF in Listing One requires a macro for readability, but it is possible to write a readable countof function using just templates [2]. The first function in Listing Five shows one way. You could call it like this:

// ... a[] is some array ...
int ct = countof(a);
for ( int i = 0; i != ct; ++i )
 /* ... process a[i] ... */ ;

The function works, but it has two disadvantages: First, the return value cannot be used as a constant expression; and second, the function won't compile for local types.

// returns the number of elements in the passed array
template<typename T, int N>
inline int countof(const T(&)[N])
{
   return N;
}
// a struct defined such that sizeof(Size<N>::Type) == N
template<int N>
struct Size
{
     typedef char Type[N];
};
// returns an object whose size is the number of 
// elements in the passed array
template<typename T, int N>
typename Size<N>::Type& count(const T(&array)[N]); 
Listing Five

A constant expression is an expression that can be evaluated at compile time and used where the compiler expects a true constant, such as in array bounds, case labels—or for countof, compile-time assertions. The C++ rules governing constant expressions are very strict about what they can contain, and function calls are not allowed. If you need the count as a compile-time constant, this creates a quandary: You need to call a function to count the elements in the array. Having counted them, you need some way to get the result back out of the function, but you can't use the return value to get it without losing its constancy. Fortunately, the rules have a loophole.

Function calls cannot appear directly in constant expressions, but they can be used as arguments to sizeof, and the result of sizeof can, in turn, be used as a constant expression. The solution, then, is for the function to be a template that indicates the array size not by its return "value," but by its return "type." This works because finding the count is actually a compile-time operation, so the result can be used as a template parameter of the return type. The function returns an object whose size, as determined by sizeof, equals the number of elements in the array. The balance of Listing Five implements this strategy. The count function returns a Size<N>::Type object, which is defined in such a way that sizeof(Size<N>::Type) == N. Therefore, to get the number of elements in a[] as a compile-time constant, you would write sizeof(count(a)).

The other limitation of the template is with local types. These types lack external linkage, and templates cannot be instantiated on types without external linkage. The only workaround is to put the type definition in a different scope where it is no longer local. This is always possible, but less than ideal. When a program uses a simple type in only one function, it is useful to be able to define the type right there in the function.

Overall, the problems with the template solution are that the sizeof(count()) notation is a little strange for getting a compile-time constant, and that the templates can't be used on local types. More generally, a tool should not make you bend your program to accommodate it; instead, it should accommodate you. To my knowledge, all pure template-based countof solutions have these same problems. I prefer a solution that "just works," for all types, with a consistent, intuitive notation, so in this case, I prefer the macro in Listing One.

—I.J.J.


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.