C++ ARRAY_SIZE() - why you still need a macro

Now, as I'm primarily a Visual C++ developer, I use the _countof() macro which is provided by the Microsoft version of stdlib.h.

For C, this is just

#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))

but for C++ it's a template class plus a macro.

1
2
3
4
5
6
extern "C++"
{
template <typename _CountofType, size_t _SizeOfArray>
char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];
#define _countof(_Array) sizeof(*__countof_helper(_Array))
}


Now, I have seen a number of threads on this site saying how, for C++, the C version above is poor (as it allows you to use it with a pointer as well as an array) and you should use a template function instead, like this (the different examples use various names, but are othersize the same.)

1
2
template< typename T, size_t N >
size_t countof( const T (&)[N] ) { return N; }


Now this works under normal circumstances, so I wondered why Microsoft uses the more complicated form above. The answer turns out that C++ can not use the function version like this (note that C++11 is a slightly different story; see later on):

1
2
3
int foo[] = {1, 2, 3, 4};

int bar[2 * countof(foo)] = {0};


VC++ won't build the second of these two arrays:
error C2057: expected constant expression
error C2466: cannot allocate an array of constant size
error C2133: 'array2' : unknown size

And neither will g++, for a slightly different reason:
warning: ISO C++ forbids variable length array 'array2'
error: variable-sized object 'array2' may not be initialized

(If you remove the initialization from the declaration line, g++ does compile it. But only as it borrows features from C99; it's not ISO C++.)

So if you want a fully flexible version of countof (aka C++ array size, etc) you need to use the macro-plus-class mentioned approach.

For the full story, see:

How Would You Get the Count of an Array in C++?
http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx

When it come to C++11, it does not have a problem with the template function if you add the constexpr to the function declaration.

1
2
[code]template< typename T, size_t N >
constexpr size_t countof( const T (&)[N] ) { return N; }


but C++11 also provides the std::extent class (from <type_traits>) which achieves the same aim

1
2
3
int foo[] = {1, 2, 3, 4};

char bar[2 * extent<decltype(foo)>::value];


Andy

PS Toy example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <cstring>
using namespace std;

template< typename T, size_t N >
size_t countof_A( const T (&)[N] ) { return N; }

template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&array)[N] ))[N];
#define countof_B( array ) (sizeof( _ArraySizeHelper( array ) ))

int main() {

    const char array1[] = "hello!";

    const size_t repeats = 3;

    // VC++ won't build this line
    // error C2057: expected constant expression
    // error C2466: cannot allocate an array of constant size
    // error C2133: 'array2' : unknown size
    //
    // nor g++
    // warning: ISO C++ forbids variable length array 'array2'
    // error: variable-sized object 'array2' may not be initialized

    /*
    char array2[3 * countof_A(array1)] = {0};
     */

    // But this line is ok
    char array2[repeats * countof_B(array1)] = {0};

    // array2 is taken to be zeroed
    for(int i = 0; i < repeats; ++i) {
        if(0 < i) {
            strcat(array2, " ");
        }
        strcat(array2, array1);
    }

    cout << array2 << endl;

    return 0;
}


Output:

hello! hello! hello!

Last edited on
Topic archived. No new replies allowed.