Доступ к членам в структуре C++ как динамически, так и статически
Я хотел бы иметь структуру (или что-то подобное) в C++, которая позволит динамически получать доступ к ее членам. Он должен иметь общий метод получения и установки, которые получают имя члена в виде строки и возвращают какой-то вариант типа (например, boost::variant
).
Я думал, что это может быть реализовано с помощью boost::fusion::map
путем добавления строки, представляющей имя каждого члена, и построения карты STL между строками и функциями получения или установки. Я не хочу изобретать велосипед, поэтому я надеялся, что нечто подобное уже существует.
Как вы думаете? Будет ли моя идея работать? Вы знаете другие способы достижения моей цели?
Спасибо Аггей
3 ответа
Fusion - это подход, но почему бы не сохранить ваши "поля" в std::map
под ключ std::string
где полезная нагрузка является boost::variant
...
т.е.
struct generic
{
std::map<std::string, boost::variant<foo, bar, bob, int, double> > _impl;
};
и тогда вы можете просто найти ключ в вашем получателе / установщике...
черт возьми, заверните variant
в optional
и вы можете иметь дополнительные поля!
более сложный пример:
class foo
{
public:
typedef boost::variant<int, double, float, string> f_t;
typedef boost::optional<f_t&> return_value;
typedef map<string, return_value> ref_map_t;
foo() : f1(int()), f2(double()), f3(float()), f4(string()), f5(int())
{
// save the references..
_refs["f1"] = return_value(f1);
_refs["f2"] = return_value(f2);
_refs["f3"] = return_value(f3);
_refs["f4"] = return_value(f4);
_refs["f5"] = return_value(f5);
}
int getf1() const { return boost::get<int>(f1); }
double getf2() const { return boost::get<double>(f2); }
float getf3() const { return boost::get<float>(f3); }
string const& getf4() const { return boost::get<string>(f4); }
int getf5() const { return boost::get<int>(f5); }
// and setters..
void setf1(int v) { f1 = v; }
void setf2(double v) { f2 = v; }
void setf3(float v) { f3 = v; }
void setf4(std::string const& v) { f4 = v; }
void setf5(int v) { f5 = v; }
// key based
return_value get(string const& key)
{
ref_map_t::iterator it = _refs.find(key);
if (it != _refs.end())
return it->second;
return return_value();
}
template <typename VT>
void set(string const& key, VT const& v)
{
ref_map_t::iterator it = _refs.find(key);
if (it != _refs.end())
*(it->second) = v;
}
private:
f_t f1;
f_t f2;
f_t f3;
f_t f4;
f_t f5;
ref_map_t _refs;
};
int main(void)
{
foo fancy;
fancy.setf1(1);
cout << "f1: " << fancy.getf1() << endl;
fancy.set("f1", 10);
cout << "f1: " << fancy.getf1() << endl;
return 0;
}
Для этого я использовал список типов, похожий на boost::cons, в котором содержатся мои члены и какое-то описание. Затем я строю это отображение, последовательно добавляя своих членов в структуру данных "метаинформация" с помощью "связанных" вызовов функций. Все это выглядит очень похоже на определение класса в boost.python. Если вы на самом деле используете boost::cons, то он также должен работать как последовательность в boost.fusion, так что вы можете легко перебирать свои данные. Возможно, вы можете использовать карту boost.fusion вместо этого, чтобы получить время доступа к log (n) во время выполнения, но кажется, что их размер ограничен до тех пор, пока не появятся различные шаблоны.
Вы просите Reflection в C++, который, я думаю, недоступен. Вам придется придумать что-то свое.