Reputation: 7151
I know you can't convert a pointer-to-member to a pointer-to-non-member (e.g., void*
), but can you convert among pointers-to-member for the same class? E.g.: for some class C
and types T
and U
, can you convert a T C::*
to a U C::*
?
I want to be able to map string names to pointers-to-member for some class. For example, given:
template<class ClassType>
struct mbr_map_traits {
typedef std::string mbr_name_type;
typedef void* ClassType::*any_mbr_ptr;
typedef std::map<mbr_name_type,any_mbr_ptr> map_type;
};
/**
* A %mbr_map is used to map a string to an arbitrary pointer-to-member of some class.
* @tparam ClassType The class whose members to map to.
*/
template<class ClassType>
struct mbr_map : mbr_map_traits<ClassType>::map_type {
typedef typename mbr_map_traits<ClassType>::mbr_name_type mbr_name_type;
/**
* Initalizes an entry in the map so as to mape \a name to a pointer-to-member.
* @param name The name to map.
* @param p The pointer-to-member to map to.
*/
template<typename MemberType>
void mbr_init( mbr_name_type const &name, MemberType ClassType::*p ) {
typedef typename mbr_map_traits<ClassType>::any_mbr_ptr any_mbr_ptr;
(*this)[ name ] = reinterpret_cast<any_mbr_ptr>( p ); // IS THIS OK?
}
/**
* Sets the value of a class member by name.
* @param c The class whose member to set.
* @param name The name of the class member to set.
* @param value The value to set the member to.
* @return true only if \a name exists in the map.
*/
template<typename MemberType>
bool mbr_set( ClassType &c, mbr_name_type const &name, MemberType const &value ) {
typedef typename mbr_map<ClassType>::const_iterator const_iterator;
const_iterator const found = this->find( name );
if ( found != this->end() ) {
typedef MemberType ClassType::*mbr_ptr;
c.*reinterpret_cast<mbr_ptr>( found->second ) = value; // IS THIS OK?
return true;
}
return false;
}
};
and some one-time initialization:
struct S {
std::string s;
int i;
bool b;
};
void mbr_map_init( mbr_map<S> *m ) {
m->mbr_init( "string_mbr", &S::s );
m->mbr_init( "int_mbr", &S::i );
m->mbr_init( "bool_mbr", &S::b );
}
I can do this:
using namespace std;
int main() {
mbr_map<S> m;
mbr_map_init( &m );
S s;
m.mbr_set( s, "string_mbr", string( "hello" ) );
m.mbr_set( s, "int_mbr", 42 );
m.mbr_set( s, "bool_mbr", true );
cout << s.s << endl;
cout << s.i << endl;
cout << s.b << endl;
return 0;
}
and it prints the values I set. But is this legal?
(The reason I want to do something like this is to map parameter names and values read from a configuration file to structure members.)
Upvotes: 0
Views: 94
Reputation: 32727
Yes it is legal. In the description for reinterpret_cast
, the language spec says,
A prvalue of type “pointer to member of X of type T1” can be explicitly converted to a prvalue of a different type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types. ... The result of this conversion is unspecified, except [converting a pointer-to-member to another pointer-to-member and back gives the original pointer to member value].
So it is allowed but may or may not do what you expect it to.
Upvotes: 0
Reputation: 44340
As long as you make a cast back to the original type before using the pointer-to-member there is no problem.
From http://en.cppreference.com/w/cpp/language/reinterpret_cast description of reinterpret_cast:
.... It is purely a compiler directive which instructs the compiler to treat the sequence of bits (object representation) of expression as if it had the type new_type.
Only the following conversions can be done....
1) An expression of integral, enumeration, pointer, or pointer-to-member type can be converted to its own type. The resulting value is the same as the value of expression.
So cast a pointer-to-member of type string to pointer-to-member of type OtherType and then at a later point casting the same pointer-to-member of type OtherType back to pointer-to-member of type string is perfectly good and won't change the original value of the pointer-to-member.
Upvotes: 1
Reputation: 1779
for some class C and types T and U, can you convert a T C::* to a U C::*?
Short answer is yes.
Long answer is of course. Data pointers, of whatever type, just give you an offset to the actual data within a structure. Type information is not encoded in the pointer - the datatype is external to actual pointer value.
Needless to say, your code is a little hairy.
Upvotes: 0