I'm working on implementing a map of member variables pointers associated with a string key. All variables range from a base class "BaseA" When accessing the variables from the map, it is only required to use the base class methods (getDesc () in the sample) so it is not necessary to retrieve the original type.
This code compiles and runs under GNU g ++ 6.2.1, but according to what I read, the use of reinterpret_cast is not portable and may not work with other compilers. Is this correct? Or does this code comply with C ++ standards? Is there any other way to do this without using the reinterpret_cast? One requirement is that "Vars" must be copyable with the default copy-contructor and copy-assignment implementations.
Sample code:
#include <iostream>
#include <sstream>
#include <map>
#include <typeinfo>
using namespace std;
struct BaseA
{
virtual string getDesc() = 0;
};
struct A1 : BaseA
{
string getDesc() override { return "This is A1"; }
};
struct A2 : BaseA
{
string getDesc() override { return "This is A2"; }
};
struct Vars
{
A1 a1;
A2 a2;
map< string, BaseA Vars::* > vars;
Vars()
{
vars["A1_KEY"] = reinterpret_cast<BaseA Vars::*>(&Vars::a1);
vars["A2_KEY"] = reinterpret_cast<BaseA Vars::*>(&Vars::a2);
}
BaseA& get( const string& key )
{
auto it = vars.find( key );
if ( it != vars.end())
{
return this->*(it->second);
}
throw std::out_of_range( "Invalid variable key:[" + key + "]");
}
};
int main()
{
Vars v;
cout << "a1 description :" << v.get("A1_KEY").getDesc() << endl;
cout << "a2 description :" << v.get("A2_KEY").getDesc() << endl;
return 0;
}
Yes, there are very few guarantees about what reinterpret_cast
will do, and casting from one pointer to member to another is not one of them (unless you then cast back to the original type, which doesn't really help you).
The safe and easy way to do this one is using std::function
:
struct Vars
{
A1 a1;
A2 a2;
map< string, std::function<BaseA&(Vars&)> > vars;
Vars()
{
vars["A1_KEY"] = &Vars::a1;
vars["A2_KEY"] = &Vars::a2;
}
BaseA& get( const string& key )
{
auto it = vars.find( key );
if ( it != vars.end())
{
return it->second(*this);
}
throw std::out_of_range( "Invalid variable key:[" + key + "]");
}
};
Note that if you never need the vars
dictionary to change, you could turn it into a static const
member. (This would mean you need to define and initialize it outside the class in a source file.)