Wednesday, 23 January 2013

Determining size of a C style array

Determining the size of a C style array at compile time is a requirement in many situations. Here are some of the possible ways to do this in C++11.


#include <iostream>

template<int N> struct ARRAYSIZEHELPER
{
    enum { mysize = N };
};
template<class T, int N> ARRAYSIZEHELPER<N> SizeHelper(T (&)[N]);


template<class T, int N> constexpr int SizeHelper2(T (&)[N])
{
    return N;
}

template<class T, int N> T (&SizeHelper3(T (&)[N]))[N];

// C++03 and C++11
#define ARRAYSIZE1(arg) (sizeof(arg)/sizeof(arg[0]))

// C++11 only
#define ARRAYSIZE2(arg) (decltype(SizeHelper(arg))::mysize)

// C++11 only
#define ARRAYSIZE3(arg) (SizeHelper2(arg))

// C++03 and C++11
#define ARRAYSIZE4(arg) (sizeof(SizeHelper3(arg))/sizeof(arg[0]))

int main()
{
    struct S {
        S() {}
        virtual void foo() {}
    private:
        int x;
    };

    // C style array of chars
    char buf1[10];

    // Using first method
    std::cout << ARRAYSIZE1(buf1) << std::endl;

    // using second method
    std::cout << ARRAYSIZE2(buf1) << std::endl;

    // using third method
    // constexpr facilitates determining size of buf2 at compile
    // time
    char buf2[2 * ARRAYSIZE3(buf1)];
    std::cout << ARRAYSIZE3(buf2) << std::endl;

    // constexpr facilitates determining size of buf2 at compile
    // time
    char buf3[2 * ARRAYSIZE3(buf2)];

    // using fourth method
    std::cout << ARRAYSIZE4(buf3) << std::endl;

    S buf4[60];
    // using fourth method
    std::cout << ARRAYSIZE4(buf4) << std::endl;
}

The first approach using ARRAYSIZE1 is the traditional approach used by lots of C and C++ code. It however lacks type safety since it can be used with a pointer argument (e.g. char *) and yield a wrong result without any indication whatsoever.

The second approach using ARRAYSIZE2 requires instantiation of ARRAYSIZEHELPER. It relies on the decltype feature of C++11. In any realistic project, this could pose a problem as it could trigger several unique instantiations of ARRAYSIZEHELPER which serve no other purpose other than returning the size of their argument. This is surely an overkill.

The third approach using ARRAYSIZE3 requires instantiation of the function template SizeHelper2 and relies on the constexpr feature of C++11. Once again, in any realistic project, this could pose a problem as it could trigger several unique instantiations of SizeHelper2 function template, which serve no other purpose other than returning the size of their argument. This is surely an overkill.

The last approach is my favourite. It translates the non type template parameter 'N' representing the size of the array function argument into the return type of the function template instantiation. Also, it just triggers the declaration of the prototype of the function template SizeHelper3 which isn't so much of a strain on the build process.

No comments:

Post a Comment