2015-07-21 66 views
1

我正在嘗試開發一個Android鍵盤,使用Android AOSP鍵盤來源作爲模型。有相當多的JNI代碼,我的C++是有點生疏,和我有以下定義麻煩宏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))) 

當我嘗試編譯,該代碼的第二行(只是#define以上)有錯誤亮起:

Declaration of reference variable requires an initializer

錯誤消息有一定的道理給我; AOSP代碼沒有。符號ArraySizeHelper在AOSP代碼中沒有其他地方出現或者生成文件(也就是說,就我可以告訴它它不是某個宏的宏而言)。

從宏的名稱,我猜測它應該計算爲數組中的元素數量。據我所知,通常的做法是:

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

所以我想知道是否有其他事情發生在這裏。

我很感激這個代碼應該做什麼的解釋,以及關於如何處理編譯錯誤的指導。

編輯:我正在通過Android Studio 1.3 RC 3,Android NDK r10e和Gradle 2.5進行編譯。編譯使用各種工具鏈(如this Android documentation中所述)。奇怪的是,上面的代碼現在編譯並正確執行(可能總是這樣做)。但是,Android studio仍然在該行上顯示錯誤。它還顯示每次使用的NELEMS的錯誤:

Error after macro substitution: Too many arguments, expected 0

我現在在想,這是一個IDE代碼分析錯誤,而不是一個編譯器或編碼問題。我原來的問題是關於代碼本身,所以我將此線程標記爲已回答。我會打開關於什麼似乎是IDE問題的另一個問題。感謝大家的解釋!

+0

如果您爲參數提供*指針*以及如何發出首個地址,請採取第二種方式嘗試並考慮其*如何工作*(但不是您想如何)。就第一個而言,您可能希望提供您正在使用的C++編譯器工具鏈信息,因爲它可以與我過去十年來使用的幾乎所有C++編譯器一起工作([見它實時](http:// ideone.com/l3OWYN))。通常會引發第一個人的事情是,實際的模板功能不需要*實現*,但它仍然有效。 – WhozCraig

+0

你到底是如何編譯它的? (較老的編譯器可能在語法上有問題。) –

+0

@AlanStokes - 我正在使用Android NDK提供的標準工具鏈。 –

回答

4

此:

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

聲明瞭一個名爲ArraySizeHelper函數,它以命名arrayNT秒的數組的引用,並返回到的char[N]數組的引用。沒有給出定義。

sizeof()不需要定義一個函數 - 它的操作數是未定義的。它只是在類型上運行:所以如果x的類型是T[N](並且不另外編譯),那麼sizeof(ArrayHelper(x))評估爲sizeof(char[N])

它也只是寫的一個可怕的複雜的方式:

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

這是遠遠更容易理解。並不需要宏。

+0

我同意今天用'constexpr'做這個評估。唉,幾乎所有OP的帖子的使用(我第一次在MS ATL 3.1中看到它)在長時間*之前都是語言功能。 (例如:您的簡化不會在VS2008上編譯; OP的第一個代碼*將*)。 – WhozCraig

+0

'constexpr'代碼看起來不錯,但是沒用,因爲標準禁止你傳遞一個實參(左值)。愚蠢。是! –

+0

@ Cheersandhth.-Alf平淡無味,因爲我非常喜歡那種簡化。 – WhozCraig

4

該代碼的目的是爲了安全地獲得可在編譯時使用的數組大小,例如,作爲新的原始(非動態)數組的大小。

簡單定義

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

&hellip;是不安全的,因爲你可以傳遞一個指針,並返回一個無意義的大小。

所以你的代碼,

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

&hellip;使用模板參數推導到找到的大小,並且它使用引用到數組的返回類型來將大小報告爲編譯時間常量。如果不是(1)在C++ 11及以後版本中關於傳遞引用的愚蠢措辭,我們現在可以對constexpr執行相同的操作。唉,我們可能必須等到C++ 17才能完全避免使用宏來獲取編譯時數組大小的簡單任務。


我無法重現該問題;以下代碼:

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; 
} 

與Visual C++ 2015和MinGW-64 g ++ 5.1.0很好地編譯在一起。


1) C++ 14§5.19/ 2 「 甲條件表達式e芯常量表達式除非的e評價,繼 抽象機的規則(1.9)將評估以下表達式之一:[&hellip;] - 一個id表達式,它引用引用類型的變量或數據成員,除非引用具有前面的初始化和 - 使用常量表達式或 進行初始化 - 它是一個對象的非靜態數據成員,其對象的生命週期始於e的評估; 」

+0

感謝您的解釋。我懷疑它可能與類型安全有關,但我的C++過於生疏,無法弄清楚發生了什麼。 –