2010-03-13 2 views
2

Мне нужно начать с кода, потому что я не уверен, какую терминологию использовать. Допустим, у меня есть следующий код:назначить член на основе значения строки

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

То, что я терпеть не могу это повторение, если (зЬгстр (attr-> имя(), «х»)!) Преобразовательной >> х; Я чувствую, что это склонность к ошибкам и монотонность, но я не могу придумать другой способ сопоставить строковое значение с назначением члена. Каковы некоторые другие подходы, которые можно предпринять, чтобы избежать такого кода? Единственный возможный альтернативный я мог думать только о том, чтобы использовать 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

Для разметки кода выберите код и нажмите кнопку «101» в редакторе в следующий раз. –

+0

Ой, как это делается, спасибо, noscript блокировал googelapi, и я всегда был в замешательстве! – Apeiron

+0

В вашем втором решении есть ошибка. 'std :: map :: operator []' создаст запись с указанным ключом, если ее еще нет, поэтому, если ваш код встречается с именем атрибута, который отсутствует на карте, он попытается разыменовать недавно созданный указатель (который будет инициализирован NULL, я уверен). Вы можете избежать этого, используя 'iter = assignMap.find (attr-> name()); if (iter! = assignMap.end()) converter >> * (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 было бы присвоить x, y и z ссылки (float&) на элементы массива 0, 1, 2 или (мои предпочтения) всегда вызывать их по номеру не по имени.

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; 
}; 
Смежные вопросы