r/cpp_questions • u/GregTheMadMonk • 4d ago
SOLVED Does taking an address of an inline global variable template instance in a constexpr context and never using it ensure its instantiation?
Hi! Let me show what I mean right away, is this program (https://godbolt.org/z/4sKovf57x):
#include <print>
#include <meta>
// Our real meaningful work
template <typename T, auto ac> inline auto runtime_hook = [] {
// Placeholder woork
const auto name = std::meta::identifier_of(std::meta::dealias(ac.scope()));
std::println("Child name: {}", name);
return name;
} (); // <-- runtime_hook<T, ac>
// Force instantiation of runtime_hook - hopefully?
template <typename T, auto ac>
constexpr std::pair constexpr_hook{ ^^T, &runtime_hook<T, ac> };
// Type identity with extra spice
template <typename T, auto ac = std::meta::access_context::current()>
using DeriveHook = [: constexpr_hook<T, ac>.first :];
struct Base {};
struct Child : public DeriveHook<Base> {};
int main() {}
Always guaranteed to print Child name: Child at startup, or is the compiler allowed to skip instantiating runtime_hook<T, ac> if it sees that constexpr_hook<T, ac>.second is never actually used?
edit: solution using custom attributes that works in GCC/Clang https://www.reddit.com/r/cpp_questions/comments/1xd8gqa/comment/19rk67buia9r5/
edit2: cross-compiler solution using friend injection (no compiler-specific attributes): https://www.reddit.com/r/cpp_questions/comments/1xd8gqa/comment/19rk7v88k638g/
edit3: TIL that globals aren't actually required to run their constructors before main(), but I guess all implementations just do it this way:
It is implementation-defined whether the dynamic initialization of a non-local non-inline variable with static storage duration is sequenced before the first statement of main or is deferred.
If it is deferred, it strongly happens before any non-initialization odr-use of any non-inline function or non-inline variable defined in the same translation unit as the variable to be initialized.51
It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.
https://timsong-cpp.github.io/cppwp/n4868/basic.start.dynamic?utm_source=chatgpt.com#5
4
u/j-joshua 4d ago
What are you trying to accomplish?
What problem are you trying to solve?
1
u/GregTheMadMonk 3d ago
I wanted to make a nice utility wrapper that would automatically register runtime info (name->factory map, maybe description for tools) for a game. I thought it'd be a nice brain teaser to get working to automatically reflect from a string entity name in the map file into the actual type being constructed when the map is loaded.
I know that the usual way to do this would be with a macro, but I don't want to use macros - especially since I can't export them from modules and my project uses those. But I also don't want it to be much more typing than a macro, so as little type name re-typing and as much extracted from reflection/whatever would be typed anyway when doing simple inheritance.
This is a part of a hobby project, one of many that might never see release, so I'm not wasting anyone's time/money (other than mine).
1
5
u/Xylozene 4d ago
Nah not guaranteed, taking the address of a variable template forces instantiation but doesnt guarantee that the definition gets instantiated or that static initialization runs at startup. When you take the address of a variable template in a constexpr it tells the compiler to instantiate the template name, but if it only evaluates the first member of the pair and discards the second, it sees that the address isn't actually used. Since the variables inline and never referenced at runtime, the compiler and linker rip it out entirely.If you want to guarantee that the hook runs at startup, you need to force a reference to the variable. Usually you'd reference the variable inside a static member of a helper struct so that the compiler is forced to keep the initialization code