Paul J. Lucas
Paul J. Lucas

Reputation: 7151

Converting among pointers-to-member

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

Answers (3)

1201ProgramAlarm
1201ProgramAlarm

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

4386427
4386427

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

user3427419
user3427419

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

Related Questions