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



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

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 类型的变量,然后使用复制语义返回转换后的值。在上面的例子中,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 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;
}

相关内容

  • 没有找到相关文章

最新更新