2017-09-13 46 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使用的是完全相同的情况下是合理的这样做的:编译器可能会或可能不会优化掉包含违规呼死代码。您的程序仍处于违规状态,另一个实施可能会拒绝它。

    相关问题