Ricky
Ricky

Reputation: 883

In C++ Is there anyway to call an object attribute through a string?

Say I have an object defined like this:

struct Something
{
    int attribute1;
    string attribute2;
}

I then have a File that contains a bunch of information that should be applied to a created object. However, the attribute name that it should be applied to is also stored in the file. In other words, the text file would contain two values like so:

123, "attribute1"

I need a way to reference an object's attribute by a string. Something like Something[variable_holding_attribute_name] would be perfect!

Is there any way of doing this in C++? Please also note that I can't use a map, as the object contains more than 1 data type.

Upvotes: 1

Views: 1420

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595742

Just because your struct uses different data types does not mean you cannot use a std::map to access them, because you can. Try something like this:

struct Something
{
    int attribute1;
    std::string attribute2;
};

void set_attr1(Something &obj, const std::string &value)
{
    std::istringstream iss(value);
    iss >> obj.attribute1;
}

void set_attr2(Something &obj, const std::string &value)
{
    obj.attribute2 = value;
};

typedef void (*set_func)(Something&, const std::string&);

std::map<std::string, set_func> m;
m["attribute1"] = &set_attr1;
m["attribute2"] = &set_attr2;

...

Something obj;

std::string value = ...; // "123"
std::string name = ...; // "attribute1"

m[name](obj, value);
/*
Or safer:
std::map<std::string, set_func>::iterator iter = m.find(name);
if (iter != m.end())
    iter->second(obj, value);
*/

If you want something a little more flexible that allows you to re-use a given function for multiple fields of the same data type, even re-use the same functions for maps of different structs, you can do this instead:

template<typename ObjType, typename MemType, MemType ObjType::*member>
void set_member(ObjType &obj, const std::string &value)
{
    std::istringstream iss(value);
    iss >> obj.*member;
}

template<typename ObjType, std::string ObjType::*member>
void set_str_member(ObjType &obj, const std::string &value)
{
    obj.*member = value;
}

template<typename ObjType>
struct set_member_hlpr
{
    typedef void (*func_type)(ObjType&, const std::string&);
};

struct Something
{
    int attribute1;
    std::string attribute2;
};

std::map<std::string, set_func_hlpr<Something>::func_type > m;
m["attribute1"] = &set_member<Something, int, &Something::attribute1>
// you can use set_member() for Something::attribute2, but
// std::istringstream will split the input value on whitespace,
// which may not be desirable. If you want to preserve the whole
// value, use set_str_member() instead..
m["attribute2"] = &set_str_member<Something, &Something::attribute2>;

...

Something obj;

std::string value = ...; // "123"
std::string name = ...; // "attribute1"

m[name](obj, value);
/*
Or safer:
std::map<std::string, set_func>::iterator iter = m.find(name);
if (iter != m.end())
    iter->second(obj, value);
*/

Upvotes: 1

Related Questions