Every compiler conditional in the library lives in one file. Adding a toolchain is one file to read.
deps/embedded_types/include/embed_compiler_directives.h holds every #if that asks about a compiler. No other file in the library tests for GNU, Clang or a version. That is a rule, not a tendency, and it has two consequences worth stating:
That header belongs to embedded_types, a separate library MMgr consumes. The macros below carry its EMBED_ prefix because they are its, and the header states the reason for using them directly: "Do not copy them into your own tree. Copies drift when this header changes."
Two further rules govern what is allowed in that file.
A directive is a request the compiler may refuse. Everything there has a fallback that still compiles. Where the absence of the directive would silently break the layout, the fallback is policed by a static assert at the point of use and not trusted.
A directive is not an operation. Nothing in that file computes anything. The library asks for no __builtin_ at all. See Doing eight bytes at a time for the measurements behind that.
| macro | with the attribute | without |
|---|---|---|
| EMBED_INLINE | static inline __attribute__((always_inline)) | static inline |
| EMBED_TABLE_STORAGE | static const | |
| EMBED_UNUSED | __attribute__((unused)) | empty |
| EMBED_WEAK | __attribute__((weak)) | empty |
| EMBED_ALIAS | __attribute__((may_alias)) | empty |
| EMBED_ALIGN(bytes_) | __attribute__((aligned(bytes_))) | empty |
| EMBED_FLATTEN | __attribute__((flatten)) | empty |
| EMBED_ENUM_BYTE(name_, ...) | a one-byte enum | constants and unsigned char |
| EMBED_BEGIN_DECLS / EMBED_END_DECLS | extern "C" { / } under C++ | empty under C |
| EMBED_STATIC_ASSERT | static_assert or _Static_assert | #error below C11 |
MMgr adds three of its own, in include/mmgr.h, for what the compiler offers and this library needs: MMGR_ERROR_ATTR puts our own text on a reference that must not link, MMGR_ALLOC_SIZE states which argument gives the extent of what an allocation returns, and MMGR_EXTRAM_ATTR is the placement the port fills in.
EMBED_HAS_ATTRIBUTE is the gate. It uses __has_attribute where that exists and falls back to EMBED_GNU_ATTRIBUTES where it does not.
An enum width is the exception to "every fallback still compiles and that is enough". An enum that silently widens to int moves every field after it in every struct containing one, and this library computes offsets from those structs.
So EMBED_ENUM_BYTE declares every such enum with one byte on every compiler. Clang on the MSVC ABI and C++ take the fixed underlying type : unsigned char, every other compiler with the packed attribute takes __attribute__((packed)), and MSVC's C, which has neither, gets the enumerators as constants and the name as an unsigned char. A struct holding one is laid out the same whichever compiler reads it.
There is a probe, in embed_types.h:
If a compiler accepted the attribute and ignored it, the build stops at the source of the problem with a message that names the cause, instead of producing a binary whose struct offsets are wrong.
The dispatch tables are addressed by offset. A positional initializer mis-wires silently when a member moves. EMBED_TABLE_LAYOUT(Table_, ...) expands to a chain of static assertions pinning each named member to its own position, in order, and pinning sizeof(Table_) to exactly that many pointers.
It is variadic up to 24 members via EMBED_NARG and EMBED_TABLE_SLOTS_1…EMBED_TABLE_SLOTS_24. That 24 is also the ceiling on a dispatch table: verbum_scrutor splits into three tables partly because of it.
Every table is pure function pointers, and so sizeof(Table_) against EMBED_NARG * EMBED_FUNCTION_POINTER_BYTES is the whole assertion. A table carrying trailing state would need its own variant testing offsetof instead.
This is why SORT_MEMBER_DOCS is NO in docs/Doxyfile: the documented order is the asserted order. See The dispatch table idiom.
EMBED_BIG_ENDIAN derives from __BYTE_ORDER__ where the compiler defines it, and falls to 0 where it does not. It exists so the library can take the cheap path when a requested order matches the host. It is never there for a caller to ask for "whatever this machine does". Wire formats state their order. See Width, alignment and byte order.
The DMA module's hardware entries are EMBED_WEAK. A board support file overrides them by defining the same symbol. Without an override they are present and refuse every request, and so memoriam_praetereo builds and its tests link on a host with no DMA controller.