文字列の値に基づいてメンバーを割り当てます
-
19-09-2019 - |
質問
私が使用してどのような用語を確認していないため、コードで始める必要があります。私は、次のコードを考えてみましょう。
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;
};
私は立つことができないことであれば(!strcmpの(attr->名()、 "×"))コンバータ>> xの繰り返しがあります。私は、これはエラーが発生しやすいと単調であることを感じて、私はメンバーの割り当てに文字列値をマッピングするための別の方法を考えることはできません。 1は、このようなコードを避けるために取ることができるいくつかの他の方法は何ですか?私は考えることができる唯一の他の可能な選択肢は、ハッシュマップを使用していたが、それは、コールバック
の問題に実行されますこれはとまで私ができる最善の方法ですが、それは私が好きなように柔軟ではありません。
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;
};
解決
マップと実装のためには、ポインタ・ツー・メンバーを使用することができます。その後、マップ(あなたがそれをコピーする場合、マップ内のポインタは、まだ元のノードに指す)のディープコピーを必要としない、それはまた、このマップが当たり、不要である(静的全部を行うことができます-instance基礎)。
例
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;
<時間>
このは、しかし、任意のより柔軟ではありません。
メンバーの様々なタイプをサポートするには、「サポートされているすべてのメンバー型の変異体」に文字列のマップを使用するには、上記を変更することがあります。
たとえばそう。部材セッターの訪問者が再利用可能であるべきであり、コードの唯一の変更は、追加したり、メンバーの種類を変更すること、の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を書くことができます。例えばストリームを格納し、指定されたメンバーのために適切な型の値を抽出しようとします。
他のヒント
配列を使用してください。この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;
};