我需要从代码开始,因为我不确定使用什么术语。假设我有以下代码:
class Node
{
public:
void Parse(rapidxml::xml_node<> *node)
{
for (rapidxml::xml_attribute<> *attr = node->first_attribute();
attr;
attr = attr->next_attribute())
{
std::stringstream converter;
converter << attr->value();
if( !strcmp(attr->name(), "x") ) converter >> x;
else if( !strcmp(attr->name(),"y") ) converter >> y;
else if( !strcmp(attr->name(), "z") ) converter >> z;
}
}
private:
float x;
float y;
float z;
};
我不能忍受的是重复if(!strcmp(attr-&gt; name(),“x”))转换器&gt;&gt; X;我觉得这容易出错并且单调,但我想不出将字符串值映射到成员赋值的另一种方法。可以采取哪些其他方法来避免这样的代码?我能想到的唯一其他可能的替代方法是使用hashmap,但这会遇到回调问题
这是我能做到的最好的,但它并不像我想的那样灵活:
class Node
{
Node() : x(0.0f), y(0.0f), z(0.0f)
{
assignmentMap["x"] = &x;
assignmentMap["y"] = &y;
assignmentMap["z"] = &z;
}
public:
void Parse(rapidxml::xml_node<> *node)
{
for (rapidxml::xml_attribute<> *attr = node->first_attribute();
attr;
attr = attr->next_attribute())
{
map<std::string, float*>::iterator member = assignmentMap.find(attr->name());
//check for a pre-existing entry
if( member == assignmentMap.end()) continue;
std::stringstream converter;
converter << attr->value();
converter >> *(member->second);
}
}
private:
float x;
float y;
float z;
std::map<std::string, float*> assignmentMap;
};
答案 0 :(得分:4)
对于使用地图的实现,您可以使用指向成员的指针。然后你不需要地图的深层副本(当你复制它时,地图中的指针仍然指向原始节点),它也将允许你使整个事物是静态的(这个地图是不必要的per - 实例基础)。
例如:
class Node {
//...
static std::map<std::string, float Node::*> initVarMap();
static float Node::* varFromName(const std::string& name);
};
std::map<std::string, float Node::*> Node::initVarMap()
{
std::map<std::string, float Node::*> varMap;
varMap["x"] = &Node::x;
varMap["y"] = &Node::y;
varMap["z"] = &Node::z;
return varMap;
}
float Node::* Node::varFromName(const std::string& name)
{
static std::map<std::string, float Node::*> varMap = initVarMap();
std::map<std::string, float Node::*>::const_iterator it = varMap.find(name);
return it != varMap.end() ? it->second : NULL;
}
用法:
float Node::* member(varFromName(s));
if (member)
this->*member = xyz;
但这并不是更灵活。
要支持不同类型的成员,您可以修改上述内容以使用字符串映射到“所有受支持的成员类型的变体”。
例如如此。成员setter访问者应该是可重用的,并且应该对typedef进行代码的唯一更改,添加或更改成员类型。
#include <map>
#include <string>
#include <iostream>
#include <boost/variant.hpp>
template <class Obj, class T>
struct MemberSetter: boost::static_visitor<void>
{
Obj* obj;
const T* value;
public:
MemberSetter(Obj* obj, const T* value): obj(obj), value(value) {}
void operator()(T Obj::*member) const
{
obj->*member = *value;
}
template <class U>
void operator()(U Obj::*) const
{
//type mismatch: handle error (or attempt conversion?)
}
};
class Node
{
public:
Node() : i(0), f(0.0f), d(0.0f)
{
}
template <class T>
void set(const std::string& s, T value)
{
std::map<std::string, MemberTypes>::const_iterator it = varMap.find(s);
if (it != varMap.end()) {
boost::apply_visitor(MemberSetter<Node, T>(this, &value), it->second);
} //else handle error
}
void report() const
{
std::cout << i << ' ' << f << ' ' << d << '\n';
}
private:
int i;
float f;
double d;
typedef boost::variant<int Node::*, float Node::*, double Node::*> MemberTypes;
static std::map<std::string, MemberTypes> initVarMap();
static std::map<std::string, MemberTypes> varMap;
};
int main()
{
Node a;
a.set("i", 3);
a.set("d", 4.5);
a.set("f", 1.5f);
a.report();
}
std::map<std::string, Node::MemberTypes> Node::initVarMap()
{
std::map<std::string, Node::MemberTypes> varMap;
varMap["i"] = &Node::i;
varMap["f"] = &Node::f;
varMap["d"] = &Node::d;
return varMap;
}
std::map<std::string, Node::MemberTypes> Node::varMap = Node::initVarMap();
这自然只是您可以做的一个例子。您可以编写static_visitor来执行您想要的操作。例如,存储流并尝试为给定成员提取正确类型的值。
答案 1 :(得分:0)
使用数组。此union
的替代方法是让x
,y
和z
成为数组元素0,1,2的引用(float&
- 或者(我的偏好)总是用数字而不是名字来称呼它们。
class Node
{
public:
void Parse(rapidxml::xml_node<> *node)
{
std::stringstream converter;
for (rapidxml::xml_attribute<> *attr = node->first_attribute();
attr;
attr = attr->next_attribute())
{
if ( strlen( attr->name() ) != 1
|| *attr->name() < 'x' || *attr->name() > 'z' )
throw rapidxml::parse_error; // or whatever
converter << attr->value() >> ary[ *attr->name() - 'x' ];
}
}
private:
union {
float ary[3]; // this can come in handy elsewhere
struct {
float x;
float y;
float z;
} dim;
};