2017-09-13 49 views
2

所以,我遇到了一個問題,我不確定它是語言問題還是編譯器/ GCC問題。C++ - 是否必須定義所有靜態類方法,即使未使用?

TL; DR - 我是否需要定義一個類中所有靜態方法,即使這些靜態方法不會被調用應用程序(即可以被鏈接器合法下降呢)?

我有一個庫類,在微控制器中實現UART的設備驅動程序。因爲我不希望多個UART對象指向相同的資源,所以每個UART對象都是一個單獨的對象,使用幾種GetInstance()方法之一檢索,每個對象用於設備中的每個UART實例(UART0,UART1等)。每個UART實例需要有兩個FIFO(Tx和Rx)用於存儲。每個FIFO需要通過應用明確地確定大小,並且在UART對象被實例化(理想地)時被分配。所以我也有一些靜態的GetStorage()方法,每個UART還有一次。

我爲概念驗證創建了一些精簡代碼。這裏的static_instance.h:

#ifndef STATIC_INSTANCE_H_ 
#define STATIC_INSTANCE_H_ 

#ifdef __cplusplus 
#include <vector> 
namespace foo { 
class Uart { 
public: 
    /* Retrieve a singleton instance, using lazy static initialization. Note 
    * that not all instances will be present for a given device. */ 
    static Uart& Uart1GetInstance(void); 
    static Uart& Uart2GetInstance(void); 
    static Uart& Uart3GetInstance(void); 
    /* Does something. */ 
    void DoSomething(void) { ++counter; } 
private: 
    /* Structure for the storage that each static Uart instance requires. */ 
    struct Storage { 
    Storage(std::vector<char>& vector) 
     : my_vector_(vector) { } 
    std::vector<char>& my_vector_; // Buffer for data. 
    }; 
    /* Instantiate object using provided register base and FIFO structures. */ 
    Uart(int instance, Storage& storage) 
     : instance_(instance), storage_(storage) { } 
    ~Uart() { } 
    /* Retrieves the storage required for the static Uart object instances. 
    * These methods are NOT implemented in static_instance.cc, but must be 
    * implemented in the application code, only for those Uart instances 
    * that are invoked in the application. */ 
    static Storage& Uart1GetStorage(void); 
    static Storage& Uart2GetStorage(void); 
    static Storage& Uart3GetStorage(void); 
    int const instance_; // Instance number of this object. 
    Storage& storage_;  // Allocated storage for this object. 
    int counter = 0;  // Dummy counter. 
}; 
} // namespace foo 
#endif // __cplusplus 

#endif 

而這裏的static_instance.cc:

#include <static_instance.h> 
namespace foo { 

Uart& Uart::Uart1GetInstance(void) { 
    static Uart uart(1, Uart1GetStorage()); 
    return uart; 
} 
Uart& Uart::Uart2GetInstance(void) { 
    static Uart uart(2, Uart2GetStorage()); 
    return uart; 
} 
Uart& Uart::Uart3GetInstance(void) { 
    static Uart uart(3, Uart3GetStorage()); 
    return uart; 
} 

} // namespace foo 

的想法是,你只叫GetInstance()你實際需要的UART實例,然後只定義GetStorage()爲UART實例。 (在這個例子中,我只定義了一個緩衝區,並使用std::vector<char>作爲替身。)此外,由應用程序來定義存儲方法,因爲每個應用程序都將有自己的要求鑑於UART的緩衝區需要。 (我是絕對不會做的是把宏在我的C++模塊,因爲,EW)下面是main.cc的代碼片段實例UART2:

namespace foo { 

Uart::Storage& Uart::Uart2GetStorage(void) { 
    static std::vector<char> rx_vector(256, 0); 
    static Uart::Storage storage(rx_vector); 
    return storage; 
} 
static foo::Uart& uart_ = foo::Uart::Uart2GetInstance(); 
void wibble(void) { 
    uart_.DoSomething(); 
} 

} // namespace foo 

現在,我正在開發使用較早的應用此芯片早期的IDE(Kinetis Design Studio v3.2.0爲好奇),它使用GCC 4.8.4並編譯鏈接沒有錯誤

但恩智浦已棄用KDS另一個工具鏈(MCUXpresso 10.0),它使用GCC 5.4.1,並使用完全相同的代碼,這個時候我得到兩個連接錯誤

./source/static_instance.o: In function 'foo::Uart::Uart1GetInstance()': 
../source/static_instance.cc:5: undefined reference to 'foo::Uart::Uart1GetStorage()' 
./source/static_instance.o: In function 'foo::Uart::Uart3GetInstance()': 
../source/static_instance.cc:13: undefined reference to 'foo::Uart::Uart3GetStorage()' 

我不確定鏈接器爲什麼會關心針對UART1和UART3的GetStorage()方法沒有定義,因爲我沒有在我的應用程序中爲UART1或UART3調用GetInstance(),因此也不會調用相應的GetStorage()方法。

我在這裏的問題是...... C++ 11 要求我有我的可執行文件中定義的所有三種存儲方法嗎?也就是說,GCC 4.8.4讓我逃避了一些本不應該的東西?或者這是我需要切換的一些GCC 5.4選項,以允許我從類中刪除未使用的靜態成員?

如果答案是「你必須定義它們,不管」,那麼我會定義它們,或者設計一些其他方式來允許。如果答案是「應該沒問題」,而且也沒有選擇,我可以在命令行設置,使GCC 5.4的做吧,然後我會採取下一步行動,並在NXP論壇報告錯誤。謝謝。

+1

爲什麼不暴露'CreateInstance'函數,而不是要求用戶代碼來定義(一些)功能? http://coliru.stacked-crooked.com/a/5e84854041391a9c – aschepler

+0

@aschepler - 我喜歡這種模式。但是說我有一個板對象,並且我想通過調用'GetInstance(2)'來初始化'Uart&'引用。我如何確保'CreateInstance(2)'將在該對象引用被初始化之前被調用一段時間? –

回答

4

TL; DR - 我是否需要定義一個類中的所有靜態方法,即使這些靜態方法永遠不會被應用

您可能需要定義這樣的靜態方法調用,即使如果他們從未被應用程序調用過。

但是,在一般情況下,你可能不需要,如果這些功能都沒有odr-used(一個定義規則)。對於靜態成員函數,這等同於

其名稱顯示爲潛在評估表達

的差別是細微的函數。我希望這表明它:

if(false) 
    function(); 

function不會被調用,而是出現在潛在評估表達,因此使用ODR-,因此必須予以界定。


我的問題在這裏...沒有C++ 11要求我在我的可執行文件中定義的所有三種存儲方式?

是的。它們都出現在潛在評估的表達式中,因此它們被使用,因此必須定義。

我正在開發一個早期的應用程序使用... GCC 4.8.4和編譯和鏈接沒有錯誤。

Odr衝突具有未定義的行爲,這就解釋了爲什麼您沒有在其他工具鏈中收到錯誤/警告。

2

[basic.def.odr]

  • 每個程序應包括每一個非內聯函數或變量,它是在 該程序ODR-使用的正好一個定義被拋棄的陳述之外(9.4.1);不需要診斷。
  • 這是來自相對較新的草案標準,但所有版本都包含類似的聲明。

    「無診斷所需的」條款給你的編譯器允許接受,即使它違反了規則,你的程序。不可達的代碼內的ODR使用的是完全相同的情況下是合理的這樣做的:編譯器可能會或可能不會優化掉包含違規呼死代碼。您的程序仍處於違規狀態,另一個實施可能會拒絕它。

    相關問題