1. 13
  1.  

  2. 7

    One loosely related trick I found quite recently is that C++ lets you template on array size, which lets you overload things that take a buffer + size to only take the buffer (can’t do it in plain C), and also lets you implement an ARRAY_COUNT macro that doesn’t compile if you use it on pointers.

    template< typename T, size_t N >
    char ( &ArrayCountObj( const T ( & )[ N ] ) )[ N ];
    #define ARRAY_COUNT( arr ) ( sizeof( ArrayCountObj( arr ) ) )
    

    I believe that declares a function returning an array of N chars? Anyway the error you get is crap but it does work (int * a; ARRAY_COUNT( a )):

    main.cc: In function ‘int main()’:
    main.cc:5:57: error: no matching function for call to ‘ArrayCountObj(int*&)’
     #define ARRAY_COUNT( arr ) ( sizeof( ArrayCountObj( arr ) ) )
                                                             ^
    main.cc:9:9: note: in expansion of macro ‘ARRAY_COUNT’
      return ARRAY_COUNT( a );
             ^~~~~~~~~~~
    main.cc:4:9: note: candidate: template<class T, long unsigned int N> char (& ArrayCountObj(const T (&)[N]))[N]
     char ( &ArrayCountObj( const T ( & )[ N ] ) )[ N ];
             ^~~~~~~~~~~~~
    main.cc:4:9: note:   template argument deduction/substitution failed:
    main.cc:5:57: note:   mismatched types ‘const T [N]’ and ‘int*’
     #define ARRAY_COUNT( arr ) ( sizeof( ArrayCountObj( arr ) ) )
                                                             ^
    main.cc:9:9: note: in expansion of macro ‘ARRAY_COUNT’
      return ARRAY_COUNT( a );
             ^~~~~~~~~~~
    
    1. 4

      Cool! If you can use a C++14 compiler (C++11 is probably good enough, but can’t recall the history of constexpr rules), you can write a constexpr function template which does the same:

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

      (ideone)

      As for the error message, I wonder if you could do something nasty with SFINAE and static_assert to improve that.

      1. 1

        OT, we need colorized syntax highlighting on Lobsters.

        1. 2

          Unfortunately C++ (e.g. template< typename T, size_t N > char ( &ArrayCountObj( const T ( & )[ N ] ) )[ N ];) is unreadable with syntax highlighting too.

        2. 1

          Note that with some GCC extensions you can implement an array-count macro that rejects pointers (or any non-array type, really) in C…from the Linux kernel:

          #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
          #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
          #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
          #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
          
        3. 4

          This old Reddit comment explains what’s going on: https://www.reddit.com/r/programming/comments/1scchh/the_difference_between_arr_and_arr_how_to_find/cdwjgup/

          tl;dr: the size of the array is contained in the array pointer’s type

          1. 1

            I guess it could be used as a more succinct way of getting a pointer to the end of a C array. Though it’s mostly in C++ that you use begin and end instead of pointer and size (and in C++ you have std::end for this):

            int arr[3] = { 7, 3, 5 };
            
            // These all do the same    
            std::sort(arr, (&arr)[1]);
            std::sort(arr, arr + sizeof(arr) / sizeof(arr[0]));
            std::sort(std::begin(arr), std::end(arr));