如何使用模板解码将来自 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转换后的值。在上面的例子中,T
是 EngineNode *
,它是一个指向 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();
// ...
};
这样,当您的 EngineNode
在 YamlConfig::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;
}
我一直在尝试为我自己的数据类型实现一些 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转换后的值。在上面的例子中,T
是 EngineNode *
,它是一个指向 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();
// ...
};
这样,当您的 EngineNode
在 YamlConfig::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;
}