微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

用谷物序列化非默认可构造类型的优雅方式

如何解决用谷物序列化非默认可构造类型的优雅方式

我使用 cereal 来序列化基于组件的游戏引擎中的对象。 游戏场景包含每个角色都有一个根组件,每个组件都可以有它的子组件。
组件只能通过提供对该组件所属的 Actor 的引用来构建(因此它不是认可构建的)。
Cereal 可以(反)序列化智能指针(或它们的向量)并在运行时推导出它们的派生类型。但它要求类型是认可构造的,而组件不是。
AFAIK 将智能指针序列化为谷物中不可构造的认类型的唯一方法是将静态方法 load_and_construct 添加到类或专用结构谷物::LoadAndConstruct。

我设法让它工作并且所有组件都正确序列化,但是这种 load_and_construct 方法迫使我将相同的方法复制并粘贴到我创建的每个组件派生类中。

ActorsAndComponents.h

#include <vector>
#include <memory>
#include <iostream>
#include <cereal/access.hpp>
#include <cereal/types/polymorphic.hpp>

class Component;
class Actor;

struct ComponentConstructData
{
    static Actor* currentActorPtr;  //Since load_and_construct must be static,we need a static pointer to construct components.
};

class Actor
{
public:
    Actor();
    Component* getRoot();
    template <typename Archive> void serialize(Archive& ar)
    {
        ComponentConstructData::currentActorPtr = this; //All components constructed from Now on using a load_and_construct method will belong to this actor.
        ar(this->RootComponent);    //No default constructor,so load_and_construct is called (see Component::serialize)
        ComponentConstructData::currentActorPtr = nullptr;  //Load_and_construct is not valid from Now on; no Component can exist without an Actor.
    }

private:
    std::unique_ptr<Component> RootComponent;
};

class Component
{
public:
    Component(Actor& actor,const std::string& name = "unnamed_comp") : ActorRef(actor),Name(name) {}
    template <typename Archive> void serialize(Archive& ar)
    {
        ar( this->Name,this->ChildrenComponents);  //Note: Component and derived classes do not have a default constructor. load_and_construct must be called (instead of serialize),so we need to define it.
    }
    template <typename Archive> static void load_and_construct(Archive& ar,cereal::construct<Component>& construct)
    { //This method is repeated in each derived class... (see ModelComponent::load_and_construct)
        if (!ComponentConstructData::currentActorPtr)
            return;

        construct(*ComponentConstructData::currentActorPtr);
        construct->serialize(ar);
    }
    void addChildComponent(std::unique_ptr<Component> childComp);
    virtual void print() const;
    void printAll() const;

protected:
    Actor& ActorRef;
    std::string Name;
    std::vector<std::unique_ptr<Component>> ChildrenComponents;
};

class ModelComponent : public Component  //example of a class derived from Component
{
public:
    ModelComponent(Actor& actorRef,const std::string& name = "unnamed_model_comp",const std::string& modelPath = std::string()) : Component(actorRef,name),ModelPath(modelPath) {}
    template <typename Archive> void serialize(Archive& ar)
    {
        ar(cereal::base_class<Component>(this),this->ModelPath);
    }
    template <typename Archive> static void load_and_construct(Archive& ar,cereal::construct<ModelComponent>& construct)
    { //Repetition... We have to do this for each class derived from Component.
        if (!ComponentConstructData::currentActorPtr)
            return;

        construct(*ComponentConstructData::currentActorPtr);
        construct->serialize(ar);
    }
    virtual void print() const override;
private:
    std::string ModelPath;
};

CEREAL_REGISTER_TYPE(Component)
CEREAL_REGISTER_TYPE(ModelComponent)
CEREAL_REGISTER_polyMORPHIC_RELATION(Component,ModelComponent)

ActorsAndComponents.cpp

#include "ActorAndComponents.h"
Actor* ComponentConstructData::currentActorPtr = nullptr;

Actor::Actor():
    RootComponent(nullptr)
{
    this->RootComponent = std::make_unique<Component>(*this,"ExampleComp-Root");
}
Component* Actor::getRoot()
{
    return this->RootComponent.get();
}

void Component::addChildComponent(std::unique_ptr<Component> childComp)
{
    this->ChildrenComponents.push_back(std::move(childComp));
}
void Component::print() const
{
    std::cout << "Component " + Name + "\n";
}
void Component::printAll() const
{
    print();
    for (auto& it : ChildrenComponents)
        it->printAll();
}

void ModelComponent::print() const
{
    std::cout << "ModelComponent " + Name + " " + ModelPath + "\n";
}

main.cpp

#include "ActorAndComponents.h"
#include <fstream>
#include <cereal/archives/json.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/types/memory.hpp>
int main()
{
    std::ofstream outputSceneFile("scene.json");
    {
        cereal::JSONOutputArchive archive(outputSceneFile);

        Actor sceneRootActor;
        {
            std::unique_ptr<ModelComponent> modelComp = std::make_unique<ModelComponent>(sceneRootActor,"ExampleModelComp","models/cubeModel.obj");
            sceneRootActor.getRoot()->addChildComponent(std::move(modelComp));
        }

        archive(sceneRootActor);
    }
    outputSceneFile.close();

    std::ifstream inputSceneFile("scene.json");
    if (inputSceneFile.good())
    {
        cereal::JSONInputArchive archive(inputSceneFile);

        Actor sceneRootActor;
        archive(sceneRootActor);

        sceneRootActor.getRoot()->printAll();
    }
    inputSceneFile.close();

    return 0;
}

Load_and_construct 在 Component 中包含与 ModelComponent 中几乎完全相同的代码;唯一的区别是我们调用了不同的方法(前者为 Component::serialize,后者为 ModelComponent::serialize)。当我们有多个组件类并且看起来没有必要时,这会成为一种负担,因为组件在序列化之前实际上是可构造的——它们只需要一个对 Actor 的引用,它们可以从对其进行序列化的父组件中获取

如果不做就完美了

        ar( this->Name,so we need to define it.

我可以使用这样的东西,而完全避免专门化 load_and_construct 方法

        ar( this->Name,//Serialize Name as usual; it's default_constructible
            constructAndSerializeNonDefaultConstructible(ChildrenComponents,this->ActorRef));  //Instead of calling the default constructor for all elements in the vector,it would forward the given arguments (only one in this case - ActorRef) and serialize the elements as usual

你如何实现这个constructAndSerializeNonDefaultConstructible 函数(或一个类似的替代函数)?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。