Imitate a static constructor in C ++

This question is related to object initialization in C ++

I have a group of classes (not instances) that inherit from a common base class, and I need them to register information about themselves in the container (in particular, the map) when the program starts.

The problem is that I need it to be dynamic. A container is defined in an independent project other than classes. I would prefer to avoid creating multiple hard-coded versions of the library, one for each set of classes in each program used.

I was thinking about having a static instance of a special class in each of these subclasses, which would make registration in its constructor. However, I did not find a way to guarantee that the container will be built before the construction of these facilities.

It should also be noted that information in the container about subclasses must be available before any instance of these subclasses is created.

Is there a way to do this or simulate a static constructor in C ++ in general?

+4
source share
6 answers

You describe different problems right away. As for the specific problem with some kind of static initialization, a simple approach is to create a fake class that will do the registration. Then each of the different classes can have a member static const X , the member must be defined in the translation block, and the definition will initiate the instantiation and registration of the class.

This does not solve the complex problem, which is a fiasco of the initialization order. The language does not provide any guarantees on the order of initialization of objects in different translation units. That is, if you compile three translation units with such classes, there is no guarantee regarding the relative execution order of the fake participant. This also applies to the library: there is no guarantee that the container into which you want to register your classes has been initialized if such a container is an attribute of the global / static member.

If you have access to the code, you can change the container code to use static local variables , and this will be a step forward to ensure the initialization order. As an outline of a possible solution:

 // registry lib class registry { // basically a singleton public: static registry& instance() { // ensures initialization in the first call static registry inst; return inst; } // rest of the code private: registry(); // disable other code from constructing elements of this type }; // register.h struct register { template <typename T> register( std::string name ) { registry::instance().register( name, T (*factory)() ); // or whatever you need to register } }; // ah class a { public: static a* factory(); private: static const register r; }; // a.cpp const register a::r( "class a", a::factory ); // bh/b.cpp similar to ah/a.cpp 

Now, in this case, there is no specific registration order for classes a and b , but this may not be a problem. On the other hand, using a local static variable in the registry::instance function, the singlet must be initialized before any call to the registry::register methods (as part of the first call to the instance method),

If you cannot make this change, you are mostly out of luck, and you cannot guarantee that the registry will be created before other static member attributes (or global characters) in other translation units. If this is the case, you will have to postpone the registration of the class until the first instance and add code to the constructor of each registered class, which ensures that the class is registered before the actual construction of the object.

This may or may not be a solution, depending on whether other code creates objects of type or not. In the particular case of the factory function (the first of which came to mind), if no one else is allowed to create objects of types a or b ..., then copying the signature to the constructor calls will not be a solution either.

+1
source

This is against the OOP paradigm, but how about your static members forming a linked list driven by two global variables? You can do something like this:

 ClassRegistrator *head=NULL; ClassRegistrator *tail=NULL; struct ClassRegistrator { ... //data that you need ClassRegistrator *next; ClassRegistrator(classData ...) { if (head==NULL) head=tail=this; else { tail->next=this; tail=this; } ... //do other stuff that you need for registration } }; class MyClass { //the class you want to register static ClassRegistrator registrator; } ClassRegistrator MyClass::registrator(...); //call the constructor 

I believe that global variables, since they do not need a constructor, but are just pure data, are guaranteed to be initialized when your code runs.

Obviously this is not thread safe, etc., but should do your job.

+1
source

This is a candidate for the Singleton pattern . Basically, you want to instantiate a container when an instance of the first instance of a subclass is created. This can be facilitated by checking whether the singleton pointer is NULL in the constructor of the base class, and if so, create an instance of the container.

0
source

One idea is to pass the registration functor to classes. Each descendant will perform a function for registration. This functor can be passed in the constructor.

Example:

 struct Registration_Interface { virtual void operator() (const std::string& component_name) = 0; }; struct Base { }; struct Child1 : public Base { Child(Registration_Interface& registration_ftor) { //... registration_ftor("Child1"); } }; 
0
source

See: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14

One option is to create a container lazily when the first is added to it:

  void AddToContainer(...) { // Will be initialized the first time this function is called. static Container* c = new Container(); c->Add(...); } 

The only way to "emulate" a static constructor is to explicitly call a function to perform your static initialization. There is no other way to run code pre-code by simply linking it in a module.

0
source

You can use the initialize on first use pattern and then instantiate a static instance to ensure initialization as early as possible.

 class cExample { public : cExample() ; // Static functions here private : static bool static_init ; // other static members here } cExample::static init = false ; cExample::cExample() { // Static initialisation on first use if( !static_init ) { // initialise static members } // Instance initialisation here (if needed) } // Dummy instance to force initialisation before main() (if necessary) static cExample force_init ; 
0
source

Source: https://habr.com/ru/post/1343829/


All Articles