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.
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.
#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