[MSVC]доступ к полю структуры по имени
От: Abyx Россия  
Дата: 14.05.11 10:22
Оценка:
есть такой код

struct IConfig
{
    virtual int get_a() = 0;
    virtual void set_a(int) = 0;
    __declspec(property(get=get_a, put=set_a)) int a;

    virtual const string& get_b() = 0;
    virtual void set_b(const string&) = 0;
    //... еще десяток полей типов int, uint, bool, string

    void save();
    void reload();
};

//--- реализация ---

class Cfg : public IConfig
{
public:
    // ...

    virtual int get_a() { return a_; }
    virtual void set_a(int value) { changed(); a_ = value; }

    virtual const string& get_b() { return b_; }
    virtual void set_b(const string&) { changed(); b_ = value; controller->on_b_updated(); }
    
    // ...

    void save()
    {
        //... 
        archive.save("a", a_);
        archive.save("b", b_);
    }

    void reload()
    {
        // ...
        a_ = archive.load("a", 100 /*default value*/);
        b_ = archive.load("b", "");
    }

private:
   void changed();

   int a_;
   string b_;

   IController controller;
};


и вот мне захотелось добавить в IConfig методы
virtual int get_int(const string& fieldName) = 0;
virtual uint get_uint(const string& fieldName) = 0;
virtual string get_string(const string& fieldName) = 0;
virtual float get_float(const string& fieldName) = 0;

virtual void set_int(const string& fieldName, int value) = 0;
virtual void set_uint(const string& fieldName, uint value) = 0;
virtual void set_string(const string& fieldName, const string& value) = 0;
virtual void set_float(const string& fieldName, float value) = 0;


и заодно уменьшить количество строк кода.

При это есть ограничение что существующие get_* методы (get_a, get_b) не должны сильно потерять в производительности,
т.е. вариант int get_a() { return get_int(field_a_name); } не подходит

IConfig можно переделывать, но не очень сильно




очевидно нужна таблица типа map<string, any_field_type>
при этом any_field_type должен уметь выдавать\принимать нужный тип, и заодно сохраняться\загружаться

но при этом он должен уметь выполнять действия аналогичные тем что выполняют сеттеры и геттеры.

решение "в лоб" — any_field_type хранит указатели на геттер\сеттер
template<typename T>
struct field_type
{
    T get(IConfig* cfg) { return cfg->*getter(); }
    void set(IConfig* cfg, T value) { cfg->*setter(value); }

    void load(archive_t ar, string name, IConfig* cfg) { set(cfg, ar.load(name, default_value)); }
    void save(archive_t ar, string name, IConfig* cfg) { ar.save(name, get(cfg)); }

    T (IConfig::*getter)();
    void (IConfig::*setter)(T);
    T default_value;    
};
struct any_field_type
{
   // ...
   variant<field_type<int>, field_type<string>, ...> field;
}

тогда надо будет инициализировать таблицу:

class Cfg : public IConfig
{
public:
    Cfg()
    {
        table.add("a", &Cfg::get_a, &Cfg::set_a, 100 /*default value*/);
        // ...
    }






другое решение — отказаться от любого сложного кода в геттерах\сеттерах, сделать метод
Cfg::call_on_changed(string fieldname, function<...> handler);
тогда код будет выглядеть так

struct table_t
{
    void add(string name, field_base* field);
    map<string, field_base*> table;
};

struct field_base { /* virtual int get_int(); set_int, get_T, ... */ };
struct int_field : field_base
{
    int_field(table_t& table, string name, int default) ... { table.add(name, this); }

    virtual int get_int() { return get(); }
    virtual int set_int(Cfg* cfg, int value_) { set(cfg, value_); }

    int get() { return value; }
    void set(Cfg* cfg, int value_)
    {
        cfg->changed();
        value = value_;
        if(onChanged)
            onChanged(cfg, this);
    } 

    int value, defaultValue;
    function<void(Cfg*, int_field*)> onChanged;
    string name;
};

class Cfg : public IConfig
{
public:
    Cfg()
      : table()
      , a(table, "a", 100)
      , b(table, "b", "")
    {}

    // тут могут быть макросы
    virtual int get_a() { return a.get(this); } virtual void set_a(int value) { a.set(this, value); }
    // ...

private:
    table_t table;

    int_field a;
    string_field b;
};






какой вариант лучше?
какие еще могут быть варианты?
In Zen We Trust
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.