简体   繁体   中英

What is happening with this code?

I'm trying to develop an Android keyboard using the Android AOSP Keyboard source as a model. There's quite a bit of JNI code, my C++ is a bit rusty, and I'm having trouble with the following definition for a macro NELEMS :

// Disclaimer: You will see a compile error if you use this macro against a variable-length array.
// Sorry for the inconvenience. It isn't supported.
template <typename T, int N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define NELEMS(x) (sizeof(ArraySizeHelper(x)))

When I try to compile, the second line of this code (just above the #define ) lights up with an error:

Declaration of reference variable requires an initializer

The error message makes some sense to me; the AOSP code does not. The symbol ArraySizeHelper occurs nowhere else in the AOSP code or make files (that is, as far as I can tell it's not a macro for something else).

From the name of the macro, I would guess that it is supposed to evaluate to the number of elements in the array. As far as I know, though, the usual way to do that would be:

#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))

so I'm wondering if something else is going on here.

I'd appreciate an explanation of what this code is supposed to do and guidance on what to do about the compile error.

EDIT: I'm compiling through Android Studio 1.3 RC 3, Android NDK r10e, and Gradle 2.5. Compilation uses various toolchains (as described in this Android documentation ). Strangely, the above code now compiles and executes correctly (perhaps it always did). However, Android studio still displays an error on that line. It also displays an error on every use of NELEMS :

Error after macro substitution: Too many arguments, expected 0

I'm now thinking that this is an IDE code analysis error, not a compiler or coding problem. My original question was about the code itself, so I'm marking this thread as answered. I'll open another question about what seems to be an IDE problem. Thanks to everyone for the explanations!

The purpose of the code is to safely get an array size that can be used at compile time, eg as the size of new raw (non-dynamic) array.

The simple definition

#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))

… is unsafe because you can pass a pointer to it, and get back a nonsense size.

So the code you have,

template <typename T, int N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define NELEMS(x) (sizeof(ArraySizeHelper(x)))

… uses template argument deduction to find the size, and it uses a reference-to-array return type to report the size as a compile time constant. If not for a (1) silly wording in C++11 and onwards about passing references, we could now do the same with constexpr . Alas, we'll probably have to wait until C++17 before macros can be completely avoided for the simple task of obtaining a compile time array size.


I'm unable to reproduce the problem; the following code:

template <typename T, int N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define NELEMS(x) (sizeof(ArraySizeHelper(x)))

#include <iostream>
auto main() -> int
{
    using namespace std;
    int x[42];
    cout << NELEMS( x ) << endl;
}

compiles nicely with both Visual C++ 2015 and MinGW-64 g++ 5.1.0.


1) C++14 §5.19/2 “A conditional-expression e is a core constant expression unless the evaluation of e , following the rules of the abstract machine (1.9), would evaluate one of the following expressions: […] — an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either — it is initialized with a constant expression or — it is a non-static data member of an object whose lifetime began within the evaluation of e ;”

This:

template <typename T, int N>
char (&ArraySizeHelper(T (&array)[N]))[N];

declares a function named ArraySizeHelper that takes a reference to an array of N T s named array and returns a reference to an array of char[N] . There is no definition given.

sizeof() does not require a definition of a function - it's operand is unevaluated. It just operates on the type: so sizeof(ArrayHelper(x)) evalutes to sizeof(char[N]) if the type of x is T[N] (and doesn't compile otherwise).

It's also a horribly complex way of just writing:

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

which is far far easier to understand. And doesn't require a macro.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM