Code size
Embedded Proto is written in C++, and C++ can cost you flash you never asked for. The library itself does not use exceptions, run time type information or dynamic memory. Whether your firmware links them in anyway is decided by your compiler flags and by what the vtables of the library reference. This page lists what to check when the size of your firmware matters.
Compiler flags
Compile your C++ with -fno-exceptions and -fno-rtti. Without them GCC links the exception unwinder and a type information table for every class with virtual functions, which adds several kilobytes to any C++ firmware, also when no code throws. Most embedded IDEs, such as STM32CubeIDE and MCUXpresso, set these flags for a new C++ project. A CMake or Makefile project has to set them itself. Two more flags help: -fno-threadsafe-statics drops the guards around local static variables, and -fno-use-cxa-atexit drops the registration of static destructors.
Compile with -ffunction-sections -fdata-sections and link with -Wl,--gc-sections. The linker then removes every function which is never called, including the parts of the library your messages do not use. Let us take a look at the flags of the size comparison we maintain, which builds for a Cortex-M4:
-Os -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-use-cxa-atexit
-Wl,--gc-sections --specs=nano.specsInterface destructors
The linker can not remove a function which a vtable refers to. As of version 4.0.0 the destructors of the base classes Field, ReadBufferInterface and WriteBufferInterface are therefore protected and not virtual. A message or a buffer is destroyed as its own type, which is what happens when it lives on the stack, in a static, or in a std::unique_ptr of the concrete type. Deleting one through a pointer to the interface is a compile error rather than undefined behaviour.
A virtual destructor would place a deleting destructor in every vtable. That destructor calls operator delete, which brings free, malloc and the bookkeeping of the C library into a firmware that never frees anything. In the size comparison this was about 700 bytes of flash and 400 bytes of RAM.
Do you own messages or buffers through a pointer to the interface, for example a std::unique_ptr<MessageInterface> allocated from the heap of your RTOS? Then define VIRTUAL_DESTRUCTORS_ENABLED when building. The destructors are public and virtual again, as they were in 3.x, at the cost mentioned above. Deriving your own class from a message or a buffer does not require the define.