gpt4 book ai didi

c++ - 给定 vector 的指针作为 void * 调用 vector 的函数

转载 作者:行者123 更新时间:2023-11-30 04:56:40 27 4
gpt4 key购买 nike

我正致力于使用 ECS 原则实现游戏引擎作为练习。我当前的设计有一个 ComponentManager 类,用于存储与每种组件类型对应的所有 vector 。该类的最小版本如下所示:

class ComponentManager{
private:
std::vector<void*> componentHolder;

public:
bool destroyEntity(int entityID, componentSignature toDestroy);

template <class T>
int registerComponent();
template <class T>
bool addComponent(int entity, T initialComp);
template <class T>
bool removeComponent(int entity);
};

componentHolder 是一个 void* vector ,其中每个条目都是包含不同组件类型的 vector 。我这样做的原因是因为我想将所有组件存储在连续的内存中,但是每个组件都是不同的类型。如果我要使用指向某些基本组件类的指针 vector ,那将破坏我试图通过此 ECS 引擎利用的缓存一致性、面向数据流的优势。

此外,我的引擎旨在让其他人可以通过简单地定义一个包含他们希望该组件存储的数据的结构来创建自定义组件,并在创建新游戏“世界”(或实例,如果您愿意)时注册该组件).此注册是通过上面看到的 registerComponent() 函数完成的,为每个组件类型创建一个唯一的 id,并定义为:

template <class T> 
int ComponentManager::registerComponent(){
componentHolder.push_back(new std::vector<T>);
return type_id<T>();
}

type_id() 函数是我从 this stackexchange question 中找到的一个技巧并且允许我将组件类型映射到我用作 ComponentManager vector 中的索引的整数。这对于组件的创建/访问非常有效,因为这些函数是模板并获取传入的组件的类型(因此我可以将位于 componentHolder vector 索引处的 void* 静态转换为右侧类型),这是一个例子:

template <class T>
bool ComponentManager::addComponent(int entityID){
int compID = type_id<T>();
std::vector<T>* allComponents = (std::vector<T>*)componentHolder[compID];

if (compEntityID.find(entityID) == compEntityID.end()){
(*allComponents).push_back(T());
return true;
}
return false;
}

然而,当我想完全摧毁一个实体时,问题就来了。我用于销毁实体的函数只需要实体 ID 和存储在 gameWorld 对象中并传入的组件签名(将位翻转为 1 的位集,对应于该实体具有的组件)。但是,由于 destroyEntity函数不获取通过模板函数传递给它的类型,并且只有位集知道要销毁哪种类型的组件,我想不出一种方法来获取类型以便我可以将 void* 转换为正确的 vector .这是我希望 destroyEntity 函数执行的操作的示例:

bool ComponentManager::destroyEntity(int entityID, componentSignature toDestroy){
for (int x = 0; x < MAX_COMPONENT; x++){
if (toDestroy[x]){
std::vector<??>* allComponents = (std::vector<??>*)componentHolder[x]; // Here is where the issue lies
(*allComponents).erase((*allComponents).begin() + entityIndex);
}
}
}

例如,在组件注册期间,有没有一种方法可以存储指向每个 vector 的删除方法的函数指针,稍后我可以从 destroyEntity() 函数调用该方法?或者以某种方式存储从我在注册期间创建的整数 componentID 到类型本身的映射,并在以后使用它进行转换?游戏中的组件类型将在运行时已知,所以我觉得这应该以某种方式可行?另外需要注意的是,有一些额外的逻辑我必须弄清楚哪个实体拥有 componentHolder 中每个组件 vector 中的哪个组件,为简洁起见我省略了这些逻辑,这样就不会引起任何问题。

在此先感谢您的帮助/您可以提供的任何提示!感谢您阅读这篇长篇文章,我愿意接受建议!

最佳答案

template<class...Ts>
using operation = void(*)(void* t, void*state, Ts...);

template<class...Ts>
struct invoker{
operation<Ts...> f;
std::shared_ptr<void> state;
void operator()(void* t, Ts...ts)const{
f(t, state.get(), std::forward<Ts>(ts)...);
}
};
template<class T, class...Ts, class F>
invoker<Ts...> make_invoker(F&& f){
return {
[](void* pt, void* state, Ts...ts){
auto* pf=static_cast<std::decay_t<F>*>(state);
(*pf)( *static_cast<T*>(pt), std::forward<Ts>(ts)... );
},
std::make_shared<std::decay_t<F>>( std::forward<F>(f) )
};
}

那么这有什么帮助呢?好吧,您可以使用它按索引存储 howto erase。

    std::vector<??>* allComponents = (std::vector<??>*)componentHolder[x];  // Here is where the issue lies
(*allComponents).erase((*allComponents).begin() + entityIndex);

你想要的是 f(void*, int)执行上述操作。

template<class T>
invoker<int> erase_at_index(){
return make_invoker<std::vector<T>,int>([]( auto&&vec, int index ){
vec.erase(vec.begin()+index);
};
}

简单存储std::vector<invoker<int>> erasers; .添加新类型时,推送由 erase_at_index<T> 制作的新橡皮擦.

然后:

    erasers[x](componentHolder[x],entityIndex); 

完成。

共享指针是每种类型一次;如果开销太大,可以使用对齐存储和静态断言 F 不是太大。

关于c++ - 给定 vector 的指针作为 void * 调用 vector 的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52397451/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com