c++ 注册表模式在编译时评估

icomxhvb  于 2023-05-23  发布在  其他
关注(0)|答案(1)|浏览(170)

编辑

我得到了一些解决方案和一个明确的答案后,探索位我可以得出结论:

  • 不可能计算跨不同转换单元的某个事件的发生率并将其用作constexpr值,因为转换单元应该是独立的。
  • 编译时的注册表模式是可能的,但其大小必须预先确定(没有动态分配)。这对更干净的代码来说是一个很大的挫折
  • 如果顺序很重要(在一个注册中心内或多个注册中心之间),那么在编译时注册中心可能不是一个好主意

选择的解决方案:静态注册表依赖于静态示例化来存储std::function<void(RealRegistryHere &)>向量。
然后,您可以控制何时需要执行lambda表达式来执行实际的注册过程。
示例:(在Registerer的构造函数中调用静态注册方法)
在SomeFactory.cpp中:

static Registerer registerer([](ComponentRegistry &registry, PoolManager &manager) {
    registry.registerType<SomeComponent>(new SomeFactory(manager));
});

通过这种方式,我们可以从定义它们的地方注册它们,避免了分组注册的需要。
我正在努力设计一个注册表,它需要编译时间来注册父对象的所有工厂,但是我在网上找不到关于这个主题的资源。

部分背景

我正在从头开始做一个小游戏引擎项目,作为学习经验(没有目标在这里做一个实际的游戏)。我主要遵循经典的ECS模式,并使用de c++20标准

问题

为了处理实体,我将它们定义为一个id(size_t,它只是一个索引)和一个std::bitset,其中的每一位都告诉我们实体是否有相应的组件。
然后,您可以使用位掩码轻松地示例化/测试:

std::bitset<FACTORY_COUNT> entity = create_entity(COMPONENT_POSITION | COMPONENT_IMAGE);

if (entity & COMPONENT_POSITION) {
  //do something...
}

其中COMPONENT_POSITION和COMPONENT_IMAGE可以定义为枚举:

enum Components {
  COMPONENT_IMAGE = 1 << 0,
  COMPONENT_POSITION 1 << 1,
  COMPONENT_SOMETHING 1 << 2
  //and do on...
}

虽然这样你可以在编译时得到组件的总数,但我不喜欢这个解决方案,因为它不灵活。您需要在同一代码文件的枚举中定义所有组件,从而增加依赖性。如果需要编写一个方法registerFactories来逐个注册它们,也会出现同样的问题(尽管它比enum好得多)。
考虑到所有组件及其相关工厂在编译时都是已知的,应该可以制作类似于注册表的东西,该注册表将知道在编译时存在多少要构建的组件对象派生的工厂。然后,它可以将数字作为constexpr值返回,该值可用于示例化std::biset。
另一个可以在编译时完成的升级是将组件的注册键(显然不是枚举,我认为是一个字符串,您可以通过调用派生组件类的静态方法来访问)替换为它的位掩码值,以便运行时只做关于实体的位演算,其他的都是系统和观察者。
我尝试了一些东西通过模板与静态类方法和自定义Map(没有动态分配)没有收益。
我可以提供一些代码示例,但我不确定它们是否相关,请告诉我您是否认为相关。
在编译时进行评估时,是否可以实现类似于注册表的功能?
感谢您的时间,如果您有任何建议,请不要犹豫!

q5iwbnjs

q5iwbnjs1#

让我来看看我是否可以总结您试图通过这个原型项目进行调查的概念。
游戏引擎将有实体,每个实体都有一定数量的组件,引擎将表示为std::bitset。实体和组件在编译时都是已知的,因此您需要一种方法来计算constexpr组件的数量,以便在定义std::bitset时使用。
这是你要问的问题(或问题之一)吗?
如果是这样,我认为选择是有限的。使用枚举是简单而有效的,但它确实成为了依赖的中心点。
我认为不可能使用任何类型的注册表来生成编译时常量。注册表通常依赖于在静态创建类型时执行运行时操作--这在生成编译时结果的过程中显然太晚了。
当然,您可以创建一个std::tuple类型,然后在编译时轻松地计算类型的数量。这与枚举具有相反的依赖关系,我认为这是有益的。每个组件都必须依赖于枚举,但std::tuple定义将依赖于组件类型声明,而组件根本不依赖于std::tuple

相关问题