Search code examples
c++templatesc++11class-template

Execute code once for each C++ class template instance


Ok, this is a complicated one.

I have a C++ class template that is instanciated many times. For each of these instances I need to execute a function that register some operators. This needs to be done only once per template instance before the first object of that template instance is used (which does not mean it must be executed at instanciation which happens during compile time).

Up to date I did this manually. But it is a pain. So I would like to execute the registration function automatically.

My current idea is to call a guarded registration method in the constructor. However this requires a (small) overhead whenever an instance of the class is contructed. Since this is done very often I would like to avoid that overhead.

I also tried to use a static RAII helper member but static template class members are not constructed if they are not actively accessed so this try failed.

Is there a way to execute code on class template instanciation (by a function or maybe by a RAII helper class) without a runtime overhead?


Solution

  • You can add a static data member which will perform whatever is needed in its constructor and destructor. You can safely put its definition into a header file because as long as it's template, it will be defined only once. The only catch is that to instantiate it you have to odr-use it.

    template <typename T>
    class Data {
    public:
        Data() {
            std::cout << "creating Data<" << typeid(T).name() << '>' << std::endl;
        }
        ~Data() {
            std::cout << "destroying Data<" << typeid(T).name() << '>' << std::endl;
        }
    };
    
    template<typename T>
    class A {
        static Data<T> data;
    public:
        A() {
            // this is necessary for data to be instantiated
            (void)data;
        }
     };
    
    // This also should be in a header
    template<typename T>
    Data<T> A<T>::data;
    
    int main(){
        A<int> aInt;
        A<int> aInt2;
        A<float> aFloat;
    }
    

    Demo

    EDIT: This is actually a bit unsafe because the order of creation of static objects in different translation units is unspecified, so for example there may be no std::cout at the moment of exetucion of Data::Data() (so don't use any static global objects there). A safer approach is to call a static function in A::A(), though it introduces some overhead:

    template<typename T>
    class A {
        static void createData() {
            static Data<T> data;
        }
    public:
        A() {
            createData();
        }
    };