如何避免模板实例化和符号表发出的C++代码膨胀?

Joe*_*Joe 9 gcc templates bare-metal c++11

几年前我开始了一个裸机(Cortex-M)项目.在项目设置中,我们决定使用gcc工具链和C++ 11/C++ 14等启用,甚至使用C++异常和rtti.

我们目前正在使用来自launchpad.net/gcc-arm-embedded的gcc 4.9(有一些问题阻止我们目前更新到更新的gcc版本).

例如,我写了一个基类和这样的派生类(另请参见此处运行示例):

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
};
Run Code Online (Sandbox Code Playgroud)

因此,我班级的客户现在可以使用例如:

char buffer[10];
FixedMemoryStream ms1(buffer, sizeof(buffer));
ms1 << "Hello World";
Run Code Online (Sandbox Code Playgroud)

现在我想让这个类的使用更加舒适,并介绍了以下模板:

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

从现在开始,我的客户可以写:

FixedMemoryStreamWithBuffer<10> ms2;
ms2 << "Hello World";
Run Code Online (Sandbox Code Playgroud)

但从现在开始,我发现可执行二进制文件的大小越来越大.似乎gcc为每个不同的模板实例化添加了符号信息FixedMemoryStreamWithBuffer(因为我们因某种原因使用rtti).

可能有办法摆脱一些特定的类/模板/模板实例化的符号信息吗?

可以为此获得非便携式gcc解决方案.

出于某种原因,我们决定更喜欢模板而不是预处理器宏,我想避免使用预处理器解决方案.

edm*_*dmz 1

是的,有一种方法可以将必要的符号几乎简化为0:使用标准库。您的OutStream课程是 的简化版本std::basic_ostream。你OutStream::write真是公正std::basic_ostream::write等等。看看这里。溢出的处理确实很仔细,但为了完整起见,它还处理underflow数据检索的需要;您可以将其保留为未定义(也是如此virtual)。

同样,您FixedMemoryStreamstd::basic_streambuf<T>一个固定大小的(a std::array<T>)获取/放置区域。

因此,只需让您的类继承标准类,您就会减少二进制大小,因为您正在重用已经声明的符号。


现在,关于template<size_t bufferSize> class FixedMemoryStreamWithBuffer. std::array<std::uint8_t, bufferSize>这个类与指定和获取内存的方式非常相似。您无法对此进行太多优化:每个实例化都是不同的类型,并且具有所有含义。编译器无法“合并”或对它们执行任何魔法:每个实例化都必须有自己的类型。因此,要么依靠std::vector或使用一些固定大小的专用块,例如 32、128 等,并且对于介于两者之间的任何值,都会选择正确的值;这可以完全在编译时实现,因此没有运行时成本。