In modern C++ (C++20) with GCC and/or Clang, is there a way to specify the linker section of static constexpr class member variables, e.g. as in
template<size_t N>
struct StringLiteral {
char value[N];
};
template <StringLiteral S>
struct ConstexprStr {
static constexpr const char[]
value __attribute__((section("constexpr_strings")))
= S::value;
};
// now use the address of that at runtime e.g. in a logging function as follows:
void pass_static_string_to_log_backend(const char *msg){
// exposition only
indicate_a_static_string_being_logged(
reinterpret_cast<std::uintptr_t>(msg));
}
// Now we can define a log macro as follows
#define LOG(msg) \
::pass_static_string_to_log_backend( \
ConstexprStr<msg>::value \
)
int main(){
// using the above template trick, we "magically" created a
// global static constant from a local scope:
LOG("hello world");
return 0;
}
The attribute above seems to have no effect - the GCC documentation explicitly that it applies to global variables not local ones, but doesn't explicitly mention (constexpr) static member variables (and I never fully wrapped my head around the details of the C++ concept of linkage).
The background to the question is the following:
I'm experimenting with ways to optimise logging on small, memory-constrained microcontrollers. One can for example consider not to format any messages at log-time at all, just storing the addresses of the strings as well as the variables into some binary stream. An implementation of this idea is seen for example in the binary_log library.
In many cases, the log-output is never needed on the micro-controller at all, but only at a later point off-device. Now we wonder: Why store those strings in flash at all, wasting bytes that are never going to be accessed? We just need an arbitrary number to associate with each message. The challenge is to let the compiler/linker create these numbers, as well as a listing of the corresponding strings.
If I could collect those strings in a separate section, then I could move that section outside of the flash space using the linker and strip it before creating the final image. For message re-assembly I simply store the binary of that section elsewhere and read the strings from it.
Here is my full experiment on GodBolt.
An alternative approach would be to create a hash of the strings at compile time and inject the strings into the debug information somehow. But that feels much less elegant, and I have yet to find a good way to store the string in the debug info. Demangling StringLiteral<8ul>{char [8]{(char)119, (char)97, (char)114, (char)110, (char)32, (char)123, (char)125}} doesn't feel like a good way.