Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing members in a C++ struct both dynamically and statically

I would like to have a struct (or something similar) in C++, that will allow access to its members dynamically. It should have a generic getter and setters that receive the member name as a string, and return some sort of variant type (e.g. boost::variant).

I was thinking it could be implemented using boost::fusion::map, by adding a string representing the name of each member, and building an STL map between strings and getter or setter functions. I don't want to reinvent the wheel, so I was hoping something similar already existed.

What do you think? Would my idea work? Do you know other ways to accomplish my goal?

like image 980
haggai_e Avatar asked Dec 02 '10 12:12

haggai_e


3 Answers

fusion is an approach, but why not store your "fields" in a std::map keyed by a std::string, where the payload is the boost::variant...

i.e.

struct generic
{
std::map<std::string, boost::variant<foo, bar, bob, int, double> > _impl;
};

and then you can just lookup the key in your getter/setter...

heck, wrap the variant in an optional and you could have optional fields!

a more complex example:

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;
}
like image 196
Nim Avatar answered Nov 06 '22 13:11

Nim


You are asking for Reflection in C++ which I think is not available. You will have to come up with something of your own.

like image 40
Aamir Avatar answered Nov 06 '22 13:11

Aamir


What I did for this was a boost::cons-like type-list that contains my members and some kind of description. I then build this mapping by successively adding my members to a "meta-info" data structure by "chained" function calls. The whole thing looks very similar to defining a class in boost.python. If you actually use boost::cons, it should also work as a sequence in boost.fusion, so you can iterate nicely over your data. Maybe you can use a boost.fusion map instead to get log(n) access times at run-time, but it seems their size is limited until variadic templates are available.

like image 37
ltjax Avatar answered Nov 06 '22 14:11

ltjax