Search code examples
c++boostboost-fusion

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?


Solution

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