How to avoid bloating C ++ code released using a template and character table?

A few years ago I started a project in the form of pure metal (Cortex-M). When setting up the project, we decided to use the gcc toolchain with C ++ 11 / C ++ 14, etc., and even to use C ++ and rtti exceptions.

We are currently using gcc 4.9 from launchpad.net/gcc-arm-embedded (with some issue that is stopping us from currently updating a later version of gcc).

For example, I wrote a base class and a derived class (see also the example below here ):

class OutStream { public: explicit OutStream() {} virtual ~OutStream() {} OutStream& operator << (const char* s) { write(s, strlen(s)); return *this; } virtual void write(const void* buffer, size_t size) = 0; }; class FixedMemoryStream: public OutStream { public: explicit FixedMemoryStream(void* memBuffer, size_t memBufferSize): memBuffer(memBuffer), memBufferSize(memBufferSize) {} virtual ~FixedMemoryStream() {} const void* getBuffer() const { return memBuffer; } size_t getBufferSize() const { return memBufferSize; } const char* getText() const { return reinterpret_cast<const char*>(memBuffer); } ///< returns content as zero terminated C-string size_t getSize() const { return index; } ///< number of bytes really written to the buffer (max = buffersize-1) bool isOverflow() const { return overflow; } virtual void write(const void* buffer, size_t size) override { /* ... */ } private: void* memBuffer = nullptr; ///< buffer size_t memBufferSize = 0; ///< buffer size size_t index = 0; ///< current write index bool overflow = false; ///< flag if we are overflown }; 

So that clients of my class can now use, for example:

 char buffer[10]; FixedMemoryStream ms1(buffer, sizeof(buffer)); ms1 << "Hello World"; 

Now I would like to make the use of the class more convenient and introduced the following pattern:

 template<size_t bufferSize> class FixedMemoryStreamWithBuffer: public FixedMemoryStream { public: explicit FixedMemoryStreamWithBuffer(): FixedMemoryStream(buffer, bufferSize) {} private: uint8_t buffer[bufferSize]; }; 

And now my clients can write:

 FixedMemoryStreamWithBuffer<10> ms2; ms2 << "Hello World"; 

But now I have seen an increase in the size of the binary executable. It seems that gcc has added character information for every other instance of the FixedMemoryStreamWithBuffer template (because for some reason we use rtti).

Could there be a way to get rid of symbol information only for some specific instances of classes / templates / templates?

It is normal to get a non-portable gcc solution for this.

For some reason, we decided to prefer templates instead of preprocessor macros; I want to avoid a preprocessor solution.

+8
gcc c ++ 11 templates bare-metal
source share
2 answers

Yes, there is a way to bring the necessary characters to almost 0 : using the standard library. Your OutStream class is a simplified version of std::basic_ostream . Your OutStream::write really just std::basic_ostream::write and so on. Take a look at this here . However, overflow occurs very closely, but for completeness, it also deals with underflow that is, the need to extract data; you can leave it as undefined (it is virtual too).

Likewise, your FixedMemoryStream std::basic_streambuf<T> with a fixed-size receive / place scope (a std::array<T> ).

So, just make your classes inherit from the standard ones and you will crop the binary size, since you are reusing already declared characters.


Now, with regard to template<size_t bufferSize> class FixedMemoryStreamWithBuffer . This class is very similar to std::array<std::uint8_t, bufferSize> , like how memory is defined and acquired. You cannot optimize much about this: each instance is a different type with all that it implies. The compiler cannot "merge" or do anything magical: each instance must have its own type. Therefore, either return to std::vector , or you have specialized pieces of a fixed size, for example 32, 128, etc., And for any values, the correct one is chosen between them; this can be achieved completely at compile time, so runtime costs are not met.

+2
source share

First of all, keep in mind that the compiler also generates a separate v-table (as well as RTTI information) for each instance of FixedMemoryStreamWithBuffer <> type, as well as for each class in the inheritance chain.

To solve the problem, I would recommend using containment instead of inheritance with some conversion function and / or statement inside:

  template<size_t bufferSize> class FixedMemoryStreamWithBuffer { uint8_t buffer[bufferSize]; FixedMemoryStream m_stream; public: explicit FixedMemoryStreamWithBuffer() : m_stream(m_buffer, bufferSize) {} operator FixedMemoryStream&() { return m_stream; } FixedMemoryStream& toStream() { return m_stream; } }; 
+2
source share

All Articles