9

我目前在STM32平台上参与嵌入式C++开发。我们的团队正在评估使用模板来参数化各种低级硬件设备的驱动程序。

所有有效的模板特化都是预先知道的,因此我们可以在实现文件中显式地给出所有有效的特化(实现和声明分开)。事实上,显式特化对我们来说非常有用,因为它有助于记录可行的参数集。

// file i2c_dev.h

template<typename traits>
struct i2c_dev
{
public:
   static void init();
   static void send();
   static bool busy();
   ...
private:
   static i2c_transfer periodic_transfer; // class with used-defined constructor
};

// traits class for configuration A
struct i2c_dev_traitsA
{
   enum 
   { 
       I2Cx_BASE    = I2C1_BASE
     , PORTx_BASE   = GPIOB_BASE
     , PORTx_PIN_TX = PB08
     , PORTx_PIN_RX = PB09
   };
};

// traits class for configuration B, different I2C peripherial and pinout
struct i2c_dev_traitsB
{
   enum 
   { 
       I2Cx_BASE    = I2C2_BASE
     , PORTx_BASE   = GPIOA_BASE
     , PORTx_PIN_TX = PA01
     , PORTx_PIN_RX = PA02
   };
};

// file i2c_dev.cpp

// Implementation of template functions
template<typename traits> 
void i2c_devy<traits>::init() { ... }

...

// explcitly specialize for all valid traits classes
template class i2c_dev<i2c_dev_traitsA>;
template class i2c_dev<i2c_dev_traitsB>;

尽管通常只会实际使用其中一种特化,但为未使用的特化生成的代码会被链接器从最终映像中删除,这正是我们想要的。

但是,每个模板特化的静态成员变量(在上面的示例中)仍保留在可执行文件中,从工具periodic_transfer生成的内存映射中可以看出。arm-none-eabi-nm这可能是因为它i2c_transfer不是 POD,而是具有用户定义的构造函数。当构造函数被移除,将事物变成 POD 类型时,静态成员也消失了。

有没有办法删除显式实例化但未使用的模板的静态非 POD 成员?

问候, 阿恩

编辑#1:重新考虑问题后,我想出了以下解决方案,这显然解决了问题。

当类i2c_transfer实际上具有它的构造函数只是为了清晰和易于使用时,将其数据成员移动到i2c_transfer_pod像这样的 POD 基类中:

 struct i2c_transfer_pod
 {
 protected:
    uint16_t m_size;
    char* m_buffer;
 };

 struct i2c_transfer : public i2c_transfer_pod
 {
 public:
    i2c_transfer();
    i2c_transfer(i2c_direction_enum dir, char*buffer, uint16_t count);

    bool failed();
    bool succeeded();
 };

然后,未使用的专业化的静态成员i2c_dev<traits>也会从最终的可执行文件中删除(如映射文件所示)。

编辑#2:虽然回答自己感觉有点蹩脚..我会请求对提议的解决方案发表评论。有没有可能更优雅的方式?编译器真的(如我所料)优化了额外的推导吗?

编辑#3:我关闭了这个问题,因为该解决方案对我有用。很高兴能更深入地了解观察到的行为的原因。

有问题的编译器是arm-none-eabi-gcc (Sourcery G++ Lite 2011.03-42) 4.5.2

4

1 回答 1

2

答案(来自编辑#1):重新考虑问题后,我想出了以下解决方案,这显然解决了问题。

当类i2c_transfer实际上具有它的构造函数只是为了清晰和易于使用时,将其数据成员移动到i2c_transfer_pod像这样的 POD 基类中:

 struct i2c_transfer_pod
 {
 protected:
    uint16_t m_size;
    char* m_buffer;
 };

 struct i2c_transfer : public i2c_transfer_pod
 {
 public:
    i2c_transfer();
    i2c_transfer(i2c_direction_enum dir, char*buffer, uint16_t count);

    bool failed();
    bool succeeded();
 };

然后,未使用的专业化的静态成员i2c_dev<traits>也会从最终的可执行文件中删除(如映射文件所示)。

于 2012-01-18T07:22:38.920 回答