所以,我遇到了一个问题,我不确定它是语言问题还是编译器/ GCC问题。
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
我们的想法是,您只需为实际需要的UART实例调用GetInstance()
,然后仅为该UART实例定义GetStorage()
。 (对于这个例子,我只定义了一个缓冲区,并使用std::vector<char>
作为替身。)此外,它还留给应用程序来定义存储方法,因为每个应用程序都是对给定的UART缓冲区需要多大有自己的要求。 (我绝对不会做的是将宏放在我的C ++模块中,因为,呃。)这是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弃用于另一个使用 GCC 5.4.1 的工具链(MCUXpresso 10.0),并使用完全相同的代码,这次我得到两个链接器错误:
./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调用GetInstance()
或者UART3在我的应用程序中,因此从不调用相应的GetStorage()
方法。
我的问题是...... C ++ 11 是否需要我在我的可执行文件中定义了所有三种存储方法?那就是GCC 4.8.4让我逃脱了我不应该做的事情吗?或者这是我需要切换的一些GCC 5.4选项,以允许我从类中删除未使用的静态成员吗?
如果答案是&#34;你必须定义它们而不管&#34;,然后我将定义它们,或者设计一些其他方式来允许。如果答案是&#34;那应该是好的&#34;,并且我无法在命令行上设置以使GCC 5.4做到这一点,那么我将采取下一步措施,报告恩智浦论坛中的错误。感谢。
答案 0 :(得分:4)
TL; DR - 我是否需要在类中定义所有静态方法,即使这些静态方法从未被应用程序调用
您可能需要定义此类静态方法,即使它们从未被应用程序调用过。
但是,一般情况下,如果这些函数不是odr-used(一个定义规则),则可能不需要。对于静态成员函数,这相当于
一个名称显示为潜在评估表达式的函数
差异很微妙。我希望这能证明这一点:
if(false)
function();
function
永远不会被调用,但会出现在潜在评估的表达式中,因此使用起来很有用,因此必须进行定义。
我的问题是...... C ++ 11是否要求我在我的可执行文件中定义所有三种存储方法?
是。它们都出现在潜在评估的表达式中,因此使用起来很有用,因此必须进行定义。
我正在使用... GCC 4.8.4开发一个早期的应用程序,并且编译和链接没有错误。
错误违规行为有未定义的行为,这就解释了为什么你没有在另一个工具链中收到错误/警告。
答案 1 :(得分:2)
[basic.def.odr]
- 每个程序应该只包含每个非内联函数或变量的一个定义 该程序在废弃的陈述之外(9.4.1);无需诊断。
醇>
这是一个相对较新的草案标准,但所有版本都包含类似的声明。
“无需诊断”子句为您的编译器提供了接受程序的权限,即使它违反了规则。如果合理的话,在无法访问的代码段中使用odr就是这样:编译器可能会也可能不会优化包含违规调用的死代码。您的程序仍然违规,另一个实施可能会拒绝它。