2017-12-19 4 views
8

몇 년 전에 베어 메탈 (Cortex-M) 프로젝트를 시작했습니다. 프로젝트 설정에서 우리는 gcc toolchain을 C++ 11/C++ 14 등으로 사용하기로 결정했고 C++ 예외 및 rtti를 사용하기도했습니다.템플릿 인스턴스화 및 기호 테이블에서 C++ 코드가 부 풀리지 않도록하려면?

우리는 현재 gcc 4.9 from launchpad.net/gcc-arm-embedded (최근 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 
}; 

: 예를 들어

, 나는 기본 클래스와 같은 파생 클래스 (또한 실행 참조 예 here)를 쓴 것

template<size_t bufferSize> class FixedMemoryStreamWithBuffer: public FixedMemoryStream { 
public: 
    explicit FixedMemoryStreamWithBuffer(): FixedMemoryStream(buffer, bufferSize) {} 
private: 
    uint8_t buffer[bufferSize]; 
}; 
: 이제
char buffer[10]; 
FixedMemoryStream ms1(buffer, sizeof(buffer)); 
ms1 << "Hello World"; 

내가 클래스의 사용이 좀 더 편안하게하고 싶은 것은 다음과 같은 템플릿을 도입 16,

그리고 지금부터, 내 고객이 쓸 수 있습니다 :

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

하지만 지금부터, 내 실행 가능한 바이너리의 크기를 증가 동안 있었던

. gcc는 FixedMemoryStreamWithBuffer의 각기 다른 템플릿 인스턴스화에 대한 심볼 정보를 추가 한 것 같습니다 (우리는 어떤 이유로 rtti를 사용하고 있기 때문입니다).

일부 특정 클래스/템플릿/템플릿 인스턴스화에 대한 심볼 정보 만 제거 할 수있는 방법이 있습니까?

휴대용이 아닌 gcc 전용 솔루션을 사용하는 것이 좋습니다.

우리는 전처리 매크로 대신 템플릿을 선호하기 때문에 전 처리기 솔루션을 피하고 싶습니다.

+0

당신이 제거하면 가상 기능 : 나는 내부의 변환 기능 및/또는 연산자로 봉쇄하는 대신 상속을 사용하는 것이 좋습니다 것입니다 문제를 해결하기 위해

? 다형성 객체 만 RTTI를 사용할 수 있습니다. – Quentin

+0

'OutStream'에서 파생 된 다른 클래스 (예 : UartStream, TcpStream 등)가 있으므로 가상 함수가 반드시 필요합니다. – Joe

+0

이러한 파생 클래스를 다형성으로 (즉, 기본 클래스의 형식을 통해) 사용하는 경우에만 필요합니다. – Quentin

답변

2

예, 표준 라이브러리를 사용하여 필요한 기호를 거의 0까지 가져 오는 방법이 있습니다. OutStream 클래스는 std::basic_ostream의 단순화 된 버전입니다. OutStream::write은 실제로는 std::basic_ostream::write입니다. 그것을보십시오 here. 오버플로는 완전히 밀접하게 처리되지만, 완전성을 위해 underflow 즉 데이터 검색의 필요성도 처리합니다. 정의되지 않은 상태로 둘 수 있습니다 (virtual도 있음).

마찬가지로, FixedMemoryStream은 고정 크기 (std::array<T>) get/put 영역의 std::basic_streambuf<T>입니다.

표준 클래스에서 클래스를 상속 받아 선언 된 심볼을 다시 사용하기 때문에 바이너리 크기를 줄일 수 있습니다.


이제 template<size_t bufferSize> class FixedMemoryStreamWithBuffer에 관해서. 이 클래스는 메모리가 지정되고 획득되는 방법과 마찬가지로 std::array<std::uint8_t, bufferSize>과 매우 유사합니다. 에 대해 그다지 최적화 할 수 없습니다. 각각의 인스턴스는 다른 유형과 모든 유형이 포함됩니다. 컴파일러는 그들에 대해 "병합"하거나 마술을 할 수 없습니다 : 각 인스턴스는 고유 한 유형을 가져야합니다. 따라서 std::vector으로 돌아가거나 32, 128 등의 고정 크기 특수 덩어리가 있고 사이에있는 값으로 올바른 것이 선택됩니다. 이것은 컴파일 타임에 완전히 달성 될 수 있으므로 런타임 비용이 들지 않습니다.모든

+0

표준 라이브러리의 코드로 OP 프로그램의 코드를 대체 할 것입니다. –

+0

내 'OutStream'과 파생 클래스 및 템플릿은 문제를 설명하기위한 예일뿐입니다. 클래스에 유사한 라이브러리가없는 경우가 있습니다. 그러나'OutStream'의 경우'stdio'를 엄격하게 사용하지 않아야합니다. 왜냐하면 엄청난 양의 바이트에 대해 코드 크기가 커지고 베어 메탈에서 대부분 비싼 힙에서 메모리를 할당하기 때문입니다. – Joe

+0

@Joe : 귀하의 코드가 iostream을 사용하는지 (사용할지) 여부에 상관없이, 제 대답의 두 번째 부분은 여전히 ​​유효합니다. BTW, 방정식에서 RTTI를 가져 와서'-nortti'를 켜보십시오. RTTI와 예외는 기호와 메모리에 많은 영향을줍니다. – edmz

2

먼저 모든 FixedMemoryStreamWithBuffer <> 상속 체인 경우뿐만 아니라, 모든 클래스의 입력을위한 컴파일러는 (RTTI 정보뿐만 아니라) 별도의 V-테이블을 생성하는 것을 명심.

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; } 
    }; 
+0

흥미로운 접근 방식. 'FixedMemoryStreamWithBuffer'는 더 이상 다형성이 아니기 때문에 컴파일러는 심볼 정보를 제거 할 수있는 진정한 기회를 갖습니다. 그러나 유감스럽게도 캐스트 연산자가이 경우 암묵적으로 작동하지 않기 때문에'to Hall() '메서드를'ms.toStream() << "Hallo World"와 같이 호출해야합니다. – Joe

+0

@Joe하지만 요청을 올바른 스트림으로 리디렉션하는 "연산자 <<"를 정의하지 못하게하는 이유는 무엇입니까? 사실, 실제 스트림처럼 동작하도록 전체 스트림 인터페이스 (flush()와 같은 함수)를 다시 정의해야한다고 가정합니다. 예, 많은 추가 작업이 필요할 수 있지만 불필요한 RTTI 및 v-table 소개 문제를 해결합니다. 고전이 말하는 것처럼 "쉬운 출구가 없다!" –

+0

그래, 지금과 미래를 위해 내 'OutStream'에서 지원하는 모든 데이터 형식에 대해'operator <<'를 복사해야합니다. 이것은 [Dry] (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)에 대한 명백한 위반입니다. – Joe

관련 문제