2011-07-29 18 views
7

Supongamos que tenemos un miembro de puntero a clase apuntando a un campo de una clase. También tenemos un puntero a ese campo específico en una instancia particular de la clase. Por ejemplo, podríamos tener algo como esto:Recuperar padre de puntero a miembro

class A { 
    B inner_object; 
} 

A* myA = /* ... */ 
B* ptr = &myA->inner_object; 
B A::* memPtr = &A::inner_object; 

¿Hay una manera de utilizar ptr y memPtr para recuperar myA? Es decir, si no tuviéramos un puntero explícito para myA, ¿podríamos hacer uno de ptr y memPtr?

+0

Podría ser más claro. No entiendo lo que quieres hacer. Lo siento. – Mahesh

Respuesta

4

Después de una buena cantidad de investigación ...

Esto se hace realmente en la mayoría de las implementaciones de lista intrusivos industriales. Sin embargo, requiere algunos hackers.

Boost estructuras intrusivas utilizan la siguiente (y sí, es la aplicación específica)

template<class Parent, class Member> 
inline const Parent *parent_from_member(const Member *member, const Member Parent::* ptr_to_member) 
{ 
    return (const Parent*)((const char*)member - 
     offset_from_pointer_to_member(ptr_to_member)); 
} 


template<class Parent, class Member> 
inline std::ptrdiff_t offset_from_pointer_to_member(const Member Parent::* ptr_to_member) 
{ 
    //The implementation of a pointer to member is compiler dependent. 
    #if defined(BOOST_INTRUSIVE_MSVC_COMPLIANT_PTR_TO_MEMBER) 
    //msvc compliant compilers use their the first 32 bits as offset (even in 64 bit mode) 
    return *(const boost::int32_t*)(void*)&ptr_to_member; 
    //This works with gcc, msvc, ac++, ibmcpp 
    #elif defined(__GNUC__) || defined(__HP_aCC) || defined(BOOST_INTEL) || \ 
    defined(__IBMCPP__) || defined(__DECCXX) 
    const Parent * const parent = 0; 
    const char *const member = reinterpret_cast<const char*>(&(parent->*ptr_to_member)); 
    return std::ptrdiff_t(member - reinterpret_cast<const char*>(parent)); 
    #else 
    //This is the traditional C-front approach: __MWERKS__, __DMC__, __SUNPRO_CC 
    return (*(const std::ptrdiff_t*)(void*)&ptr_to_member) - 1; 
    #endif 
} 

esencialmente la misma cosa (aunque en C) como se hace en el núcleo de Linux para administrar listas intrusivos, con el container_of macro (pero, por supuesto, ptr-to-members no se utilizan):

#define container_of(ptr, type, member) ({ \ 
      const typeof(((type *)0)->member) *__mptr = (ptr); 
      (type *)((char *)__mptr - offsetof(type,member));}) 
3

No es así. Un puntero a miembro no tiene conocimiento de ninguna instancia de la clase de la que es miembro. Es por eso que necesita una instancia en cualquier momento que desee acceder al miembro a través del puntero.

Un puntero a miembro es no un puntero. De hecho, probablemente fue un error del comité de C++ incluso llamarlo un puntero. En muchas (si no la mayoría) implementaciones, incluso los tamaños de un puntero no son iguales al tamaño de un puntero a miembro. No hay trucos de compensación que puedas jugar aquí. E incluso si encontrara una forma, si analizara los datos en un puntero-a-miembro, aún sería específico para esa implementación.

+1

Creo que la pregunta implícita es: ¿podemos, por ejemplo, restar uno de otro para obtener un puntero a objeto? –

+0

Ok, pero si pensamos en el ptr-to-member simplemente como un desplazamiento, el padre debe ser trivialmente recuperable. ¿Estás diciendo que un ptr-to-member no es un offset, o no se puede hacer que se comporte como uno? (EDIT: qué oli dijo) – bcr

+1

@Oli - ¿por qué dar ideas locas a la gente? :-) – littleadv

1

No puede.

Un puntero al miembro no almacena información sobre ninguna instancia en particular.

Sólo conoce un tipo y un puntero a una función dentro de ese tipo.

1

Esto definitivamente no es estándar y no muy recomendado para el uso real, pero se puede intentar esto:

A *fake_A= reinterpret_cast<A *>(1); 
B *fake_B= &(fake_A->*ptr_to_member); 
char *fake_A_raw= static_cast<char *>(static_cast<void *>(fake_A)); 
char *fake_B_raw= static_cast<char *>(static_cast<void *>(fake_B)); 

ptrdiff_t offset_to_A_from_B= fake_B - fake_A; 

char *member_raw= static_cast<char *>(static_cast<void *>(member)); 
char *base_raw= member_raw - offset_to_A_from_B; 
A *base= static_cast<A *>(static_cast<void *>(base_raw)); 

y que realmente no debería hacer esto.

+0

y por qué estás publicando esto? : P – YeenFei

+0

Jaja, sí, es un ejemplo impresionante. Pero probablemente haya formas más sencillas de hacer esencialmente lo mismo con lo que tenemos disponible, ¿no crees? Por ejemplo, si tiene un puntero a alguna estructura de tipo A, puede hacer que el desplazamiento deje de restar un puntero al campo deseado del puntero a la estructura. ¿O me estoy perdiendo algo? – bcr

+0

@bcr, cierto, pero eso no es lo que pediste. Quería saber dado un puntero a miembro cómo volver a la clase raíz; no puede convertir un puntero al miembro en un desplazamiento directamente. Por ejemplo, el puntero nulo al miembro no tiene que ser cero a nivel de bit. – MSN

0

Debe ser posible y es muy útil. El puntero al miembro es solo un desplazamiento, siempre que esté seguro de qué tipo de estructura contiene el puntero al miembro.

Cuestiones relacionadas