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 struc
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):
//
// Return the number of elements in a statically sized array.
// DWORD Buffer[100];
// RTL_NUMBER_OF(Buffer) == 100
// This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc.
//
#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0]))
#if defined(__cplusplus) && \
!defined(MIDL_PASS) && \
!defined(RC_INVOKED) && \
!defined(_PREFAST_) && \
(_MSC_FULL_VER >= 13009466) && \
!defined(SORTPP_PASS)
//
// RtlpNumberOf is a function that takes a reference to an array of N Ts.
//
// typedef T array_of_T[N];
// typedef array_of_T &reference_to_array_of_T;
//
// RtlpNumberOf returns a pointer to an array of N chars.
// We could return a reference instead of a pointer but older compilers do not accept that.
//
// typedef char array_of_char[N];
// typedef array_of_char *pointer_to_array_of_char;
//
// sizeof(array_of_char) == N
// sizeof(*pointer_to_array_of_char) == N
//
// pointer_to_array_of_char RtlpNumberOf(reference_to_array_of_T);
//
// We never even call RtlpNumberOf, we just take the size of dereferencing its return type.
// We do not even implement RtlpNumberOf, we just decare it.
//
// Attempts to pass pointers instead of arrays to this macro result in compile time errors.
// That is the point.
//
extern "C++" // templates cannot be declared to have 'C' linkage
template <typename T, size_t N>
char (*RtlpNumberOf( UNALIGNED T (&)[N] ))[N];
#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A)))
//
// This does not work with:
//
// void Foo()
// {
// struct { int x; } y[2];
// RTL_NUMBER_OF_V2(y); // illegal use of anonymous local type in template instantiation
// }
//
// You must instead do:
//
// struct Foo1 { int x; };
//
// void Foo()
// {
// Foo1 y[2];
// RTL_NUMBER_OF_V2(y); // ok
// }
//
// OR
//
// void Foo()
// {
// struct { int x; } y[2];
// RTL_NUMBER_OF_V1(y); // ok
// }
//
// OR
//
// void Foo()
// {
// struct { int x; } y[2];
// _ARRAYSIZE(y); // ok
// }
//
#else
#define RTL_NUMBER_OF_V2(A) RTL_NUMBER_OF_V1(A)
#endif
#ifdef ENABLE_RTL_NUMBER_OF_V2
#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V2(A)
#else
#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V1(A)
#endif
//
// ARRAYSIZE is more readable version of RTL_NUMBER_OF_V2, and uses
// it regardless of ENABLE_RTL_NUMBER_OF_V2
//
// _ARRAYSIZE is a version useful for anonymous types
//
#define ARRAYSIZE(A) RTL_NUMBER_OF_V2(A)
#define _ARRAYSIZE(A) RTL_NUMBER_OF_V1(A)
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()).
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:
#include <iterator>
#include <algorithm>
#include <iostream>
template<typename T>
struct StructSize
{
private: static T x;
public: enum { size = sizeof(T)/sizeof(*x)};
};
template<typename T>
struct StructSize<T*>
{
/* Can only guarantee 1 item (maybe we should even disallow this situation) */
//public: enum { size = 1};
};
struct X
{
int operator *();
};
int main(int argc,char* argv[])
{
int data[] = {1,2,3,4,5,6,7,8};
int copy[ StructSize<typeof(data)>::size];
std::copy(&data[0],&data[StructSize<typeof(data)>::size],©[0]);
std::copy(©[0],©[StructSize<typeof(copy)>::size],std::ostream_iterator<int>(std::cout,","));
/*
* For extra points we should make the following cause the compiler to generate an error message */
X bad1;
X bad2[StructSize<typeof(bad1)>::size];
}
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.
template<typename T,int SIZE>
inline size_t array_size(const T (&array)[SIZE])
{
return SIZE;
}
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).
constexpr size_t array_size(const T (&array)[SIZE])
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
#include <boost/typeof/typeof.hpp>
template<typename T>
struct ArraySize
{
private: static T x;
public: enum { size = sizeof(T)/sizeof(*x)};
};
template<typename T>
struct ArraySize<T*> {};
and is used by writing
ArraySize<BOOST_TYPEOF(foo)>::size
where foo is the name of an array.
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.