Is there a good way to set C / C ++ member variables from string representations? (Introspection-lite)

I have a structure with some members that I want to get and set from a string. Given that C ++ does not have any introspection, I suppose I need some kind of creative solution with macros, a stringize operator, and maybe boost::bind.I don’t need full serialization or introspection, more "introspection-lite"

I would like to have something like that:

struct MyType {
  int  fieldA;
  int  fieldB;
};
DECLARE_STRING_MAP(MyType,fieldA);
DECLARE_STRING_MAP(MyType,fieldB);

MyType t;
SET_VALUE_FROM_STRING(MyType,t,"fieldA","3")    

Instead of having a huge operator if.

Any idea if there is a neat solution for this?

Related question: Reflection of objects

EDIT: Thanks to maxim1000 for the "map to int Type :: *" trick - this worked for me:

#define DEFINE_LOOKUP_MAP(Type) std::map<AnsiString,int Type::*> mapper 
#define ADD_FIELD_MAPPING(Type, Field) mapper[#Field]=&Type::Field 
#define SET_FIELD_FROM_MAP(Type, Field, var, value) var.*(mapper[#Field])=value    

DEFINE_LOOKUP_MAP(MyType); 
ADD_FIELD_MAPPING(MyType, fieldA); 
ADD_FIELD_MAPPING(MyType, fieldB); 

SET_FIELD_FROM_MAP(MyType, fieldA, obj, 3);
+5
8

, - :

std::map<std::string,int MyType::*> mapper;
mapper["fieldA"]=&MyType::fieldA;
mapper["fieldB"]=&MyType::fieldB;
...
MyType obj;
obj.*(mapper["fieldA"])=3;
+5

.

, struc :

#include <map>
#include <string>
#include <sstream>

template<class STRUC>
struct Field
{
    virtual void set (STRUC& struc, const std::string& value) const = 0;
};

template<class STRUC, class FIELDTYPE>
struct FieldImpl : public Field<STRUC>
{
    typedef FIELDTYPE (STRUC::*MemberPtr);

    FieldImpl (MemberPtr memberPtr) {memberPtr_ = memberPtr;}

    virtual void set (STRUC& struc, const std::string& value) const
    {
        std::istringstream iss (value);
        iss >> struc.*memberPtr_;
    }

private:
    MemberPtr memberPtr_;
};

template<class STRUC>
class FieldMap
{
private:
    typedef std::map<std::string, Field<STRUC>*> FieldNameMap;
    FieldNameMap  fieldMap_;

public:
    ~FieldMap ()
    {
        // delete fieldMap_ members.
    }

    void bind (const std::string& name, Field<STRUC>* field)
    {
        fieldMap_[name] = field;
    }

    template<typename FIELDTYPE>
    void bind (const std::string& name, FIELDTYPE (STRUC::* member))
    {
        fieldMap_[name] = new FieldImpl<STRUC, FIELDTYPE> (member);
    }

    void setValue (STRUC& struc, const std::string& name, const std::string& value)
    {
        FieldNameMap::const_iterator iter = fieldMap_.find (name);

        if (iter == fieldMap_.end ())
            throw std::runtime_error (std::string ("No field binding found for ") + name);

        (*iter).second->set (struc, value);
    }
};

struct Test
{
    int id;
    double value;
    std::string tag;
};

int main (int argc, char* argv[])
{
    FieldMap<Test> fieldMap;
    fieldMap.bind ("id", &Test::id);
    fieldMap.bind ("value", &Test::value);
    fieldMap.bind ("tag", &Test::tag);

    Test test;

    fieldMap.setValue (test, "id", "11");
    fieldMap.setValue (test, "value", "1234.5678");
    fieldMap.setValue (test, "tag", "hello");

    return 0;
}
+4

.

, , , .

, , :

BEGIN_STRUCT(MyType)
FIELD(int, fieldA);
FIELD(int, fieldB);
END_STRUCT

# . # :

#define BEGIN_STRUCT(x) struct x {
#define FIELD(x, y) x y;
#define END_STRUCT };

# :

#define BEGIN_STRUCT(x) namespace x ## Mapping { typedef x MappedType;
#define FIELD mapper[#x]=&MappedType::x;
#define END_STRUCT }

, .

, , (Perl, Python Cog ..).

++

, ++ , . ROOT Reflex .

+2

- , - big if, , . ( ) , , .

, - , "".

//Assumption: at the time you want to use this, you've got two strings, one with the 
// name of the field to set (key), one with the value to set (value). I also assume

typedef struct MyType {
  int  fieldA;
  int  fieldB;
} MyType;

// fldnamedef - name of the field in the structure definition (const char *)
// convfunc - name of a function that takes a value, returns a fldtypedef
// s - structure to put data into
// key - const char * pointing to input field name
// value - const char * pointing to input field value
#define IF_FIELD_SET(fldnamedef, convfunc, s,  key, value) {\
  if (strcmp(#fldnamedef, key) == 0) {\
    s.fldnamedef = convfunc(value);\
  }\
}


int main()
{
  MyType t={0,0};

  IF_FIELD_SET(fieldA, atoi, t, "fieldA", "2");

  printf("%d,%d\n",t.fieldA, t.fieldB);
}

, IF_FIELD_SET:

{ if (strcmp("fieldA", "fieldA") == 0) { t.fieldA = atoi("2"); }};
+1

? , .

, :

struct MyType
{
  int fieldA;
  int fieldB;

  void setField(std::string const& field, std::string const& value);
};

setField , , . - ( ints, ... ), .

static std::map<std::string, Functor<MyType>*> M_Map;

// where Functor is

template <class Type>
struct Functor
{
  virtual void set(Type& t, std::string const& value) const = 0;
};

// And a specialization would be
struct SetfieldA : public Functor<MyType>
{
  virtual void set(MyType& t, std::string const& value) const
  {
    std::istringstream stream(value);
    stream >> t.fieldA;
    // some error handling could be welcome there :)
  }
};

std::istringstream, , std::istream. , .

, , !

, .

#define INTROSPECTED(MyType_)                                                    \
  private:                                                                       \
    typedef Functor<MyType_> intro_functor;                                      \
    typedef std::map<std::string, intro_functor const*> intro_map;               \
    static intro_map& IntroMap() { static intro_map M_; return M_; }             \
  public:                                                                        \
    static void IntroRegister(std::string const& field, intro_functor const* f){ \
      IntroMap()[field] = f; }                                                   \
    void setField(std::string const& field, std::string const& value) {          \
      intro_map::const_iterator it = IntroMap().find(field);                     \
      if (it != IntroMap().end()) it->second->set(*this, value); }

#define INTROSPECT_FIELD(Class_, Name_)                                          \
  struct Set##Name_: public Functor<Class_> {                                    \
    virtual void set(Class_& t, std::string const& value) {                      \
      std::istringstream stream(value); stream >> t.Name_; } } Setter##Name_;    \
  Class_::IntroRegister(#Name_, Setter##Name_)

:

// myType.h
struct MyType
{
  INTROSPECTED(MyType);

  int fieldA;
  int fieldB;
};

// myType.cpp
INTROSPECT_FIELD(MyType, fieldA);
INTROSPECT_FIELD(MyType, fieldB);

// Any file
MyType t;
t.set("fieldA", "3");

, : , , .

+1

, "< . , , , .

0

, .

, STL:

typedef std:: map MyType;

MyType t;

t["fieldA"] = atoi("3");
printf("%d\n", t["fieldA"]);

, , :

typedef std::map<std::string, std::string> MyType;

MyType t;
t["fieldA"] = "3";

printf("%d\n", atoi(t["fieldA"]));

get , , .

typedef std::map<std::string, std::string> MyType;
#define fieldA(v) atoi(v["fieldA"])

MyType t;
t["fieldA"] = "3";

printf("%d\n", fieldA(v));

, .

MyType . , , , ifs . , , . , . , , - .

class MyType {
public:
  set(std::string key, std::string value) {
    if (key == "fieldA") m_fieldA = atoi(value.c_str());
    if (key == "fieldB") m_fieldB = atoi(value.c_str());
  };

  int fieldA() { return m_fieldA; };
  int fieldB() { return m_fieldB; };
private:
  int m_fieldA;
  int m_fieldB;
};

MyType t;
t.set("fieldA", "3");
printf("%d\n", t.fieldA());
0

, / ? .

0

All Articles