如何使用模板解码将来自 YAML::Node 的字符串存储在 class 中

How to store a string from YAML::Node in a class using template decoding

我一直在尝试为我自己的数据类型实现一些 yaml-cpp 的转换结构。其中一个是 class,而不仅仅是一个结构。编码功能工作正常。但是解码功能没有。我尝试从 yaml 文件中获取字符串并在 class.

中设置正确的变量
template<>
struct convert<EngineNode*> {
  static Node encode(EngineNode *rhs) {
    Node node;
    std::string type;
    if(rhs->type == 0) {
        type = "node";
    } else if(rhs->type == 1) {
        type = "scene";
    } else if(rhs->type == 3) {
        type = "particle";
    }
    node[type]["name"] = rhs->name;
    node[type]["type"] = rhs->type;
    node[type]["velocity"] = rhs->getVelocity();
    node[type]["position"] = rhs->getPosition();
    node[type]["rotation"] = rhs->getRotation();

    for(unsigned i = 0; i < rhs->children.size(); i++) {
        if(rhs->children[i]->type == SPRITE) {
            node[type]["children"].push_back((SpriteNode*)rhs->children[i]);
        } else {
            node[type]["children"].push_back(rhs->children[i]);
        }
    }
    return node;
  }

  static bool decode(const Node& node, EngineNode *rhs) {
    if((!node["root"]["node"].IsDefined()) && (!node["root"]["scene"].IsDefined()) && (!node["particle"].IsDefined())) {
        return false;
    }
    std::string type;

    if(node["root"]["node"].IsDefined()) {
        type = "node";       
    } else if(node["root"]["scene"].IsDefined()) {
        type = "scene";
    }
    const Node n = node["root"][type];

    rhs->name = n["name"].as<std::string>();
    rhs->type = n["type"].as<int>();
    rhs->setVelocity(n["velocity"].as<Velocity>());
    rhs->setPosition(n["position"].as<Point>());
    rhs->setRotation(n["rotation"].as<float>());

    for(unsigned i = 0; i < n["children"].size(); i++) {
        if(n["children"]["type"].as<int>() == SPRITE) {
            rhs->addChild(n["children"].as<SpriteNode*>());
        } else {
            rhs->addChild(n["children"].as<EngineNode*>());
        }
    }

    return true;
  }
};

如果您想查看完整的源代码,请访问 github

问题是它出现以下错误的段错误:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff73e3b5c in std::string::assign(std::string const&) () from /usr/lib/libstdc++.so.6
(gdb) bt
#0  0x00007ffff73e3b5c in std::string::assign(std::string const&) () from /usr/lib/libstdc++.so.6
#1  0x000000000040f590 in YAML::convert<EngineNode*>::decode (node=..., rhs=0x8) at src/yaml_config.cpp:90
#2  0x000000000040ef9f in YAML::as_if<EngineNode*, void>::operator() (this=0x7fffffffe4c0) at /usr/include/yaml-cpp/node/impl.h:119
#3  0x0000000000407a7d in YAML::Node::as<EngineNode*> (this=0x7fffffffe5c8) at /usr/include/yaml-cpp/node/impl.h:143
#4  0x00000000004074f9 in YamlConfig::readNode (this=0x8cf810, yaml_node=...) at src/yaml_config.cpp:172
#5  0x000000000040739a in YamlConfig::read (this=0x8cf810, path=...) at src/yaml_config.cpp:161
#6  0x0000000000404fa5 in GameScene::GameScene (this=0x8d62d0) at game/gamescene.cpp:16
#7  0x0000000000404205 in Main::initGameScene (this=0x61d2b0) at src/main.cpp:75
#8  0x0000000000404480 in main () at src/main.cpp:145

我不明白为什么会这样,它可能与 yaml-cpp 根本无关,只是我对模板和此处使用的其他内容的理解非常有限。 (我只会很基础的c++)

如有任何帮助,我们将不胜感激,

彼得

问题是Node::as<T>函数在栈上分配了一个T类型的变量,然后使用复制语义return转换后的值。在上面的例子中,TEngineNode *,它是一个指向 EngineNode 的指针。 as 函数不会为实际的 EngineNode 分配额外的内存,因此取消引用(使用 *->)会导致分段错误。

解决方法是修改您的转换函数以使用引用而不是指针:

template<>
struct convert<EngineNode> {
  static Node encode(const EngineNode &rhs) {
    Node node;
    std::string type;
    if(rhs.type == 0) {
        type = "node";
    } else if(rhs.type == 1) {
        type = "scene";
    } else if(rhs.type == 3) {
        type = "particle";
    }    

    // ...
  }

  static bool decode(const Node& node, EngineNode &rhs) {
    if((!node["root"]["node"].IsDefined()) && (!node["root"]["scene"].IsDefined()) && (!node["particle"].IsDefined())) {
    return false;
    }
    std::string type;

    if(node["root"]["node"].IsDefined()) {
        type = "node";       
    } else if(node["root"]["scene"].IsDefined()) {
        type = "scene";
    }
    const Node n = node["root"][type];

    rhs.name = n["name"].as<std::string>();
    // ...
  }
};

确保您已经为您的 EngineNode class:

定义了合适的复制构造函数
class EngineNode {
public:
    EngineNode();
    EngineNode(const EngineNode &rhs); // Copy-constructor
    ~EngineNode();
// ...
};

这样,当您的 EngineNodeYamlConfig::read 中分配时,如果像这样重写,它将生成有效副本:

EngineNode *YamlConfig::read(std::string path) {
    YAML::Node doc = YAML::LoadFile(path);
    EngineNode *node = new EngineNode(doc.as<EngineNode>());

    // ...

    return node;
}

调用此函数的任何人都应该知道,这会在堆上分配一个新的 EngineNode 对象。这个对象可能需要在某个时候通过调用 delete 来释放它。或者,您可以修改 API 以使用智能指针:

std::shared_ptr<EngineNode> YamlConfig::read(std::string path) {
    YAML::Node doc = YAML::LoadFile(path);
    auto node = std::make_shared(doc.as<EngineNode>());

    // ...

    return node;
}