2010-03-13 4 views
2

어떤 용어를 사용해야할지 모르므로 코드로 시작해야합니다. 나는 다음과 같은 코드가 있다고 가정하자 : 내가 참을 수 없어하는 경우 (STRCMP (attr-> 이름(), "X")!) 컨버터 >> x의 반복이다문자열 값을 기준으로 멤버를 지정하십시오.

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; 
}; 

; 이 오류가 발생하기 쉽고 단조롭다 고 생각하지만 문자열 값을 멤버 할당에 매핑하는 다른 방법을 생각할 수 없습니다. 이 같은 코드를 피하기 위해 취할 수있는 다른 접근 방법은 무엇입니까? 내가 생각할 수있는 유일한 다른 가능한 대안은 해시 맵을 사용했지만, 그 콜백

이 내가 함께 할 수있는 최선입니다 문제로 실행하지만, 내가 원하는만큼 그것은 유연 아니다 :

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

코드 마크 업은 코드를 선택하고 다음 번에 편집기에서 '101'버튼을 사용하십시오. –

+0

오, 그게 어떻게 완료됩니다, 감사합니다, noscript는 googelapi를 차단하고 있었고 난 항상 혼란스러워했습니다! – Apeiron

+0

두 번째 해결 방법에 버그가 있습니다. 'std :: map :: operator []'는 지정된 키를 가진 엔트리가 존재하지 않는다면 그 엔트리를 생성 할 것이고, 그래서 여러분의 코드가 맵에없는 속성 이름을 가로 질러 오면, 새로 만든 포인터 (NULL로 초기화됩니다, 나는 꽤 확신합니다). iter = assignmentMap.find (attr-> name()); if (iter! = assignmentMap.end()) 변환기 >> * (iter-> second)'. –

답변

4

지도를 사용하여 구현하려면 포인터를 구성원으로 사용할 수 있습니다. 그런 다음지도의 전체 사본이 필요하지 않습니다 (복사 할 때지도의 포인터가 원본 노드를 계속 가리 킵니다). 또한 모든 것을 정적으로 만들 수 있습니다 (이지도는 인스턴스 기반). 예를 들어

:

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를 작성할 수 있습니다. 예 : 스트림을 저장하고 주어진 멤버에 대해 올바른 유형의 값을 추출하려고 시도합니다.

+0

고마워, 지금은 부스트 ​​라이브러리를 연구하고 정확하게 작동하는 방법을 찾아야 해. – Apeiron

0

배열을 사용하십시오. 이 union에 대한 대안은 배열 요소 0, 1, 2 - 또는 (내 환경 설정)에 대한 참조 (float&)를 항상 이름이 아닌 번호로 호출하도록 x, yz이되도록하는 것입니다.

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; 
}; 
관련 문제