Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removal of unused template instantiation's static members

I am currently involved embedded C++ development on am STM32 platform. Our team is evaluating the use of templates to parametrize drivers for various low-level hardware devices.

All valid template specializations are known in advance thus we can give all valid specializations explicitly inside a implementation file (implementation and declaration separated). In fact, to us explicit specialization is quite useful since it helps to document viable parameter sets.

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

Even though generally only one of the specializations will be actually used, the code generated for unused specializations gets removed from the final image by the linker, which is exactly what we want.

However, the static member variables - periodic_transfer in the above sample - of each template specialization remain in the executable, as can be seen in the memory map generated by the arm-none-eabi-nm tool. This is probably due to the fact that i2c_transfer is not a POD but has a user-defined constructor. When the constructor is removed, turning the thing into a POD type, the static members also vanish.

Is there a way to remove static non-POD members of explicitly instantiated, but unused templates?

Regards, Arne

Edit #1: After rethinking the problem I came up with the following solution, which apparently fixes the problem.

When class i2c_transfer which in fact has it's constructors only for clarity and ease of use, has its data members moved into a POD base class i2c_transfer_pod like this:

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

Then, the static members of unused i2c_dev<traits> specializations are also removed from the final executable (as the map file suggests).

Edit #2: Although answering one-self feels a bit lame.. I would kindly request comments on the proposed solution. Is there possibly a more elegant way? Is the compiler really (as I presume) optimizing away the additional derivation?

Edit #3: I close the question since the solution works for me. Would be nice to have deeper insight into the reason for the observed behaviour.

The compiler in question is arm-none-eabi-gcc (Sourcery G++ Lite 2011.03-42) 4.5.2

like image 220
Arne Avatar asked Jan 17 '12 18:01

Arne


1 Answers

Answer (from Edit #1): After rethinking the problem I came up with the following solution, which apparently fixes the problem.

When class i2c_transfer which in fact has it's constructors only for clarity and ease of use, has its data members moved into a POD base class i2c_transfer_pod like this:

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

Then, the static members of unused i2c_dev<traits> specializations are also removed from the final executable (as the map file suggests).

like image 103
Arne Avatar answered Oct 25 '22 07:10

Arne