1

I found a way I think is good for keeping track of which templates have been instantiated in my program, and I find this really useful for me:

constinit const char* loggerTypesCreated[32];
int count = 0;

template <typename T>
struct MyLogger
{
    static inline char usedVariable = rand();
    static inline char dummy = []()
    {
        loggerTypesCreated[count] = typeid(T).name();
        ++count;

        return char(); // dummy
    }();
};
    
int main()
{
    MyLogger<int>{}, MyLogger<char>{}, MyLogger<double>{}, MyLogger<float>{};

    std::cout << "Loggers created:\n";
    for (int i = 0; i < count; ++i)
        std::cout << "Logger<" << loggerTypesCreated[i] << ">\n";
}

Though the array and the static dummy in the class are both static, initialization order are defined because the array is 'constant' initialized. You could do this with an std::vector too as far as I know because the default constructor is constexpr, and initialized constantly.

Anyway, for me running a function at the start of the program in a way like above is really useful for me for registering the types I've created, but is there a way to do this without a dummy variable?

Furthermore, does the dummy variable risk getting optimized out because it's not used? Its initializer has a side effect, but I don't know if it matters.

This works great for me for a lot of things, including placing timers in functions.

Edit: In case it's not clear, I don't want the initialiser function to be called when creating an object, I want it like this:

void functionToBeTimed
{
     Timer</a_unique_type/> timer;
     // Not when the object is created, but at program startup
// to initialise all instantiated templates.
}
Zebrafish
  • 11,682
  • 3
  • 43
  • 119

1 Answers1

2

Is there a way to do this without a dummy variable?

You can provide a (optionally constexpr) constructor for MyLogger and move the definition which you have in the static immediately invoked lambda dummy.

template <typename T> struct MyLogger
{
    constexpr MyLogger() noexcept
    {    
        loggerTypesCreated[count] = typeid(T).name();
        ++count;
    }
    static inline char usedVariable = rand(); // --> do you need this??
};

This way, you update the loggerTypesCreated upon MyLogger object creation, and no worries of redundant dummy variable, and it's optimization issues.

Additionally, you can move the MyLogger<int>{}, MyLogger<char>{}, MyLogger<double>{}, MyLogger<float>{} to a templated function.

template<typename... Args>
constexpr void log_types() noexcept
{
    (MyLogger<Args>{}, ...);
}

And object creation be like:

log_types<int, char, double, float>();

See a (Demo Here)

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • Sorry, I guess I wasn't clear, that works if you construct an object, but I was wondering for just in the case that the class type is instantiated. My example is this, say you have function, at the top of the function you put Timer<"nameoffunctionasconstant"> timer_obj{ }; I only want the registration function called once on startup, that's why I have the dummy that does that. – Zebrafish Sep 04 '21 at 08:31