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
  • 10,356
  • 2
  • 35
  • 93

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
  • 26,381
  • 6
  • 42
  • 81
  • 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 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