模仿C++中的静态构造函数

发布于 2024-10-23 17:53:32 字数 332 浏览 7 评论 0原文

这是一个与 C++ 中对象的初始化相关的问题。

我有一组类(不是实例),继承自一个公共基类,并且我需要它们在程序启动时在容器(特别是映射)中注册有关自身的信息。

问题是我需要它是动态的。容器是在独立的项目中定义的,与类不同。我宁愿避免制作该库的多个硬编码版本,每个版本用于使用该库的每个程序中的每一组类。

我考虑过在每个子类中都有一个特殊类的静态实例,这将在其构造函数中进行注册。但是,我没有找到任何方法来保证容器将在这些对象的构造之前被构造。

我还应该注意,在创建这些子类的任何实例之前,容器中有关子类的信息应该可用。

有没有办法做到这一点,或者模仿 C++ 中的静态构造函数?

This a question related to the initialization of objects in C++.

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

The problem is that I need it to be dynamic. The container is defined in an independent project, different from the classes. I would prefer to avoid making multiple hard-coded versions of the library, one for each set of classes in each program that uses it.

I thought about having a static instance of a special class in each of these subclasses, that would make the registration in its constructor. However, I have found no way to guarantee that the container will be constructed before the construction of these objects.

I should also note that the information in the container about the subclasses should be available before any instance of these subclasses is created.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

拥抱我好吗 2024-10-30 17:53:32

您同时描述了不同的问题。对于某种静态初始化的特定问题,一种简单的方法是创建一个将执行注册的伪类。那么每个不同的类都可以有一个 static const X 成员,该成员必须在翻译单元中定义,并且定义将触发实例的实例化和类的注册。

这并没有解决初始化订单惨败这一难题。该语言不对不同翻译单元中对象的初始化顺序提供任何保证。也就是说,如果您使用此类类编译三个翻译单元,则无法保证假成员的相对执行顺序。这也适用于库:如果您要在其中注册类的容器是全局/静态成员属性,则无法保证该容器已被初始化。

如果您有权访问代码,则可以修改容器代码以使用静态局部变量,这将在确保初始化顺序方面向前迈出一步。作为可能的解决方案的草图:

// 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
   }
};
// a.h
class a {
public:
   static a* factory();
private:
   static const register r;
};
// a.cpp
const register a::r( "class a", a::factory );
// b.h/b.cpp similar to a.h/a.cpp

现在在这种情况下,ab 类的注册之间没有明确的顺序,但这可能不是问题。另一方面,通过在 registry::instance 函数中使用局部静态变量,可以保证单例的初始化在之前执行对 registry::register 方法的任何调用(作为第一次调用 instance 方法的一部分)。

如果您无法进行此更改,那么您基本上就不走运,并且无法保证注册表将在其他翻译单元中的其他静态成员属性(或全局变量)之前实例化。如果是这种情况,那么您必须将类的注册推迟到第一次实例化,并向每个要注册的类的构造函数添加代码,以确保该类在实际注册之前注册。对象的构造。

这可能是也不是一个解决方案,具体取决于其他代码是否创建该类型的对象。在工厂函数的特殊情况下(第一个想到的),如果没有其他方法允许创建 ab 类型的对象...那么小猪支持注册构造函数调用也不是解决方案。

You are describing different problems all at once. On the particular issue of having some sort of static initialization, a simple approach is creating a fake class that will perform the registration. Then each one of the different classes could have a static const X member, the member will have to be defined in a translation unit, and the definition will trigger the instantiation of the instance and the registration of the class.

This does not tackle the hard problem, which is the initailization order fiasco. The language does not provide any guarantee 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 on the relative order of execution of the fake member. That is also applied to the library: there is no guarantee that the container in which you want to register your classes has been initialized, if such container is a global/static member attribute.

If you have access to the code, you can modify the container code to use static local variables, and that will be a step forward as to ensure the order of initialization. As a sketch 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
   }
};
// a.h
class a {
public:
   static a* factory();
private:
   static const register r;
};
// a.cpp
const register a::r( "class a", a::factory );
// b.h/b.cpp similar to a.h/a.cpp

Now in this case there is no definite order among the registration of the a and b classes, but that might not be an issue. On the other hand, by using a local static variable in the registry::instance function the initialization of the singleton is guaranteed to be performed before any call to the registry::register methods (as part of the first call to the instance method).

If you cannot make that change you are basically out of luck and you cannot guarantee that the registry will be instantiated before the other static member attributes (or globals) in other translation units. If that is the case, then you will have to postpone the registration of the class to the first instantiation, and add code to the constructor of each class to be registered that ensures that the class is registered before actual construction of the object.

This might or not be a solution, depending on whether other code creates objects of the type or not. In the particular case of factory functions (first one that came to mind), if nothing else is allowed to create objects of types a or b... then piggy backing registration on constructor calls will not be a solution either.

宫墨修音 2024-10-30 17:53:32

这是单例模式的候选者。基本上,您希望在实例化子类的第一个实例时实例化容器。这可以通过检查基类构造函数中的单例指针是否为 NULL 来实现,如果是,则实例化容器。

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

荒人说梦 2024-10-30 17:53:32

一种想法是将注册函子传递给类。每个后代都会执行要注册的函数。该函子可以在构造函数中传递。

例子:

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");
  }
};

One idea is to pass a registration functor to the classes. Each descendant would execute the function to register. This functor could 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");
  }
};
自由如风 2024-10-30 17:53:32

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

一个选择是在添加第一件事时延迟构造容器:

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

“模仿”静态构造函数的唯一方法是显式调用函数来执行你的静态初始化。没有其他方法可以仅通过链接到模块来运行主程序前的代码。

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

One option is to construct the container lazily, when the first thing 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 "imitate" a static constructor is to explicitly call a function to perform your static initialization. There is no other way to run code pre-main just by linking in a module.

你与清晨阳光 2024-10-30 17:53:32

您可以使用“首次使用时初始化”模式,然后实例化一个虚拟静态实例以确保尽早初始化。

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 ;

You might use an "initialise on first use" pattern, and then instantiate a dummy static instance to ensure initialisation 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 ;
月野兔 2024-10-30 17:53:32

这是违反 OOP 范式的,但是让你的静态成员形成一个由 2 个全局变量引导的链表怎么样?你可以这样做:

ClassRegistrator *head = nullptr;
ClassRegistrator *tail = nullptr;

struct ClassRegistrator {
    // ... Data that you need
    ClassRegistrator *next;
    ClassRegistrator(classData ...) {
      if (!head) 
          head = tail = this;
      else {
        tail->next = this;
        tail = this;
      }
      // ... Do other stuff that you need for registration
    }
};

// The class you want to register
class MyClass { 
    static ClassRegistrator registrator;
}

ClassRegistrator MyClass::registrator(...); // Call the constructor

相信全局变量,因为它们不需要构造函数,而只是纯数据,保证在您开始执行您的代码。

显然这不是线程安全的等,但应该可以完成你的工作。

It is against OOP paradigm, but how about having your static members form a linked list guided by 2 global variables? You could do something like that:

ClassRegistrator *head = nullptr;
ClassRegistrator *tail = nullptr;

struct ClassRegistrator {
    // ... Data that you need
    ClassRegistrator *next;
    ClassRegistrator(classData ...) {
      if (!head) 
          head = tail = this;
      else {
        tail->next = this;
        tail = this;
      }
      // ... Do other stuff that you need for registration
    }
};

// The class you want to register
class MyClass { 
    static ClassRegistrator registrator;
}

ClassRegistrator MyClass::registrator(...); // Call the constructor

I believe the global variables, as they don't need have a constructor, but are just pure data, are guaranteed to be already initialised when you begin the execution of your code.

Obviously this is not thread-safe, etc, but should make your job done.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文