It's often useful to count the number of elements in an array. Using std::vector or std::array, this is done using .size() however this function is not available on good old C++ array.
Furthermore, implictily sized arrays have their sizes defined at compilation time which could be useful in many scenarios such as the ones in listing 1.
Listing 1: Example of use cases for countof:- static std::size_t const ImplictlySizedArray[] = {1, 2};
- enum{COUNT = countof(ImplictlySizedArray)};
- void foo()
- {
- std::size_t const CountOf = countof(ImplictlySizedArray);
- for(std::size_t i = 0; i < countof(ImplictlySizedArray) * 2; ++i)
- {
- switch(i)
- {
- case countof(ImplictlySizedArray):
- break;
- }
- }
- }
Quick and dirty: a macro
For compilation time determination of the size of an array, we often define a macro called countof or sometime ARRAY_SIZE as defined in listing 2.
Listing 2: Trivial countof implementation based on the C preprocessor with usage example:- #define countof(arr) sizeof(arr) / sizeof(arr[0])
- static const std::size_t values[] = {42, 76, 16, 11, 31};
- void foo()
- {
- for(std::size_t i = 0; i < countof(values); ++i)
- bar(values[i]);
- }
Unfortunately, the implementation in listing 1 has multiple drawbacks:- 1 Because macros carry no type, the implementation is not robust and bad usage won't be detected at compilation time.
- 2 Macros can't be overloaded which would be useful for containers.
- 3 Just like any macro, there are potential namespace collision issues.
To detail item 1, let's look at a possible refactoring of listing 2 in listing 3.
Listing 3: Refactoring of listing 1 leading to an invalid usage of the trivial countof implementation:- #define countof(arr) sizeof(arr) / sizeof(arr[0])
- static const std::size_t values[] = {42, 76, 16, 11, 31};
- void foo(std::size_t const arg[])
- {
- for(std::size_t i = 0; i < countof(arg); ++i)
- bar(arg[i]);
- }
- void main()
- {
- bar(values);
- }
In listing 3, arg may look like an implicitly size array but actually, it's just a pointer. Hence, when we call countof on arg we divide the size of a pointer (32 bits or 64 bits depending on the system) by the size of an element in this array which is indeed not what we expect.
Robust C++ 98 implementation
To resolve this issue, Charles Nicholson publihed a C++98 implementation with a detail description.
This implementation is shown in listing 4.
Listing 4: Implementation of countof using a C++98 trick:- template <typename T, std::size_t N>
- char(&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T(&)[N]))[N];
- #define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x))
The idea is to return the size of an array of char, declared using the template argument size (N) of the array we want to know the number of elements from.
This countof implementation still particularly relevant and work well on all static use cases.
I find only two minor draw backs. First, it's still a macro so overloading for containers will be challenging.
Second, old GCC compilers doesn't support this construct.
Visual C++ built-in macro
Visual C++ supports a built-in macro called _countof to detect invalid inputs at compilation time but this solution is not standard.
Alternative C++ 11 implementation
Fortunately, C++ 11 introduced constant expressions allowing us to create functions which executions are resolved at compilation time. This allows us to write a function for countof in listing 5 which provides type safety, namespace capability and compile time resolution.
Listing 5: Implementation of countof using a C++11 constant expression:- template <typename T, std::size_t N>
- constexpr std::size_t countof(T const (&)[N]) noexcept
- {
- return N;
- }
Listing 6: Usage examples of constant expression countof:- static const std::size_t values[] = {42, 76, 16, 11, 31};
- void foo()
- {
- for(std::size_t i = 0; i < countof(values); ++i)
- bar(values[i]);
- }
- void foo(const std::size_t arg[])
- {
- for(std::size_t i = 0; i < countof(arg); ++i)
- bar(arg[i]);
- }
Scalable implementation for old compilers support
countof implementation based on constant expressions is a great improvement over the macro implementation. However, nowadays, only a subset of compilers support constant expressions. For example, Visual Studio 2015 only introduced a subset of constant expressions support and previous versions had absolutly no support of this C++ 11 feature.
Hence, listing 7 proposes a more scalable implementation to manage the transition period until C++11 support bacomes universal.
Listing 7: Implementation of countof with fallbacks:- #ifndef __has_feature
- #define __has_feature(x) 0
- #endif
- #if __cplusplus >= 201103L || _MSC_VER >= 1900 || __has_feature(cxx_constexpr) // C++ 11 implementation
- namespace detail
- {
- template <typename T, std::size_t N>
- constexpr std::size_t countof(T const (&)[N]) noexcept
- {
- return N;
- }
- }
- #define MY_NAMESPACE_COUNTOF(arr) detail::countof(arr)
- #elif _MSC_VER
- #define MY_NAMESPACE_COUNTOF(arr) _countof(arr)
- #elif __cplusplus >= 199711L && (
- defined(__INTEL_COMPILER) || defined(__clang__) ||
- (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)))
- template <typename T, std::size_t N>
- char(&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T(&)[N]))[N];
- #define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x))
- #else
- #define MY_NAMESPACE_COUNTOF(arr) sizeof(arr) / sizeof(arr[0])
- #endif
Overloading for dynamic containers
Only when relaying on the C++11 implementation shown in listing 5, we can consider overloading the countof function to work with STL containers.
Listing 8 provides an implementation example.
Listing 8: Implementation of countof using a C++11 constant expression:- template <class C>
- std::size_t countof(C const & c)
- {
- return c.size();
- }
Toward C++ 17 support of countof
A new standard library paper titled Non-member size() and more has been adopted for C++17.
This proposal is using the same implementation as describ here.
Enjoy!
Special thanks to Gregory Pakosz, Stefan Reinalter and Don Williamson for the great comments and references.