简体   繁体   中英

Obtain a pointer to a data member by its name

Let's say my class has an integer data member:

class MyClass {

public:
    MyClass();

private:
    int my_int;
}

Now upon construction, I want to receive a data type, a name and a value, all codified as strings:

public MyClass(string type, string name, string value) { ... }

And based on type and name, assign that value to my data member on construction. Something similar to:

public MyClass(string type, string name, string value)
{
    this->parse(type, name, value);
}

private parse(string type_, string name_, string value_)
{
    if (type_=="int")
    {
        int* int_ = getDataMember(name_);
        (*int_)=atoi(value_);
    }
    else ....
}

So now when i declared MyClass c("int","my_int","5") , c would be initialized with its my_int data member set to the value of 5.

The problem is I have no idea how to implement getDataMember(string name). But it would obviously return a pointer to its data member of the name "name".

While this isn't how C++ is supposed to be used, it isn't entirely impossible.

#include <string>
#include <map>

class MyClass {

public:
    MyClass(const std::string& type, const std::string& name, const std::string& value);

private:
    void parse(const std::string& type_, const std::string& name_, const std::string& value_);
    template <typename T>
    T* getDataMember(const std::string& name);

    int my_int;
    double my_double;
    char my_char;
    std::map<std::string, char*> members; //Map of strings to member memory locations
};

MyClass::MyClass(const std::string& type, const std::string& name, const std::string& value)
{
    //List of members will have to be hardcoded
    members["my_int"] = reinterpret_cast<char*>(&my_int);
    members["my_double"] = reinterpret_cast<char*>(&my_double);
    members["my_char"] = reinterpret_cast<char*>(&my_char);
    this->parse(type, name, value);
}

template <typename T>
T* MyClass::getDataMember(const std::string& name) {
    return reinterpret_cast<T*>(members[name]); //Will need to handle invalid names
}

void MyClass::parse(const std::string& type_, const std::string& name_, const std::string& value_)
{
  if (type_=="int")
  {
    int* int_ = getDataMember<int>(name_);
    (*int_)=atoi(value_.c_str());
  }
}

int main(void) {

    MyClass c("int","my_int","5");

    return 0;
}

The idea is to keep a map to reference string s to member addresses. Accessing the map with the string will return the address of the member corresponding to that string . However, this map will have to be hardcoded when new members are introduced to the class. Also, the getDataMember function will have to handle cases where invalid names are passed into the class.

Inefficiencies

string comparisons are fundamentally slow. The comparisons occur when you are inserting members into the map , when you are going through your parse function trying to identify the type, and when you searching through the map for the correct key.

Possible ways to get rid of hardcoding

Suppose there is a way to fill the map without hardcoding. That would involve knowing what data members are present in the class. Since there does not seem to be such a functionality in C++, we would have to parse the file, or at least the part of the file that contains the class. However, we need this to be done before the file is compiled. This leaves us with 2 options:

  1. Do it at compile-time, which involves using preprocessor directives. Unfortunately, I have no idea of any way to utilize preprocessor directives to do so.
  2. Program an external executable that parses files in your project/workspace to identify the data members of the class and proceed to append the filling of the member map in the class's constructor(s). ie Automate the map filling process through an external executable. We can then ensure that this external executable is always run whenever the project is build through pre-build events.

Conclusion

I think it's best to either find another approach to the problem you are trying to solve, or use a whole other language because it seems C++ was not built for this.

Factoring in what your real problem is

I believe the solution you are looking for is called deserialization.

class MyClass {
public:
    void Deserialize(XMLParser xml);

private:
    int my_int;
    double my_double;
    char my_char;
};

void MyClass::Deserialize(XMLParser xml) {
    while(xml.ReadLine() != "MEMBERS"); //Advances the XML parser to the line that specifies the members
    //Proceed to deserialize the members in order
    //This requires the file to list the members in the correct order
    my_int = atoi(xml.ReadLine().c_str());
    my_double = atof(xml.ReadLine().c_str());
    my_char = atoi(xml.ReadLine().c_str());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM