2009-08-25 18 views
6

¿Cómo puedo hash (std :: tr1 :: hash o boost :: hash) una función de puntero a miembro de C++?¿Cómo hash y compara una función de puntero a miembro?

Ejemplo:

tengo varios bool (Class :: * functionPointer)() (no estática) que apuntan a varios métodos diferentes de la Clase clase y necesito hash de los miembro-función puntero a .

¿Cómo puedo hacer eso?

También, ¿cómo puedo comparar (std :: less) esos punteros de funciones miembro para que pueda almacenarlos en un std :: set?

+7

Normalmente no hay ninguna razón para hash un puntero, ya que apunta directamente a lo que desea acceder.Proporcione un código que ilustre lo que pregunta. –

+0

¿Cuándo dirías que un puntero de función es 'menos' que otro? –

+0

@bojan: si el único propósito de la comparación es almacenarlos en una lista ordenada, cualquier orden determinista servirá. Por ejemplo, el valor binario. – erikkallen

Respuesta

13

Todos los objetos C++, incluidos los punteros a funciones miembro, se representan en la memoria como una matriz de caracteres. Lo que podría intentar:

bool (Class::*fn_ptr)() = &Class::whatever; 
const char *ptrptr = static_cast<const char*>(static_cast<const void*>(&fn_ptr)); 

Ahora tratan ptrptr como apuntar a una serie de (sizeof(bool (Class::*)())) bytes, y el hash o comparar esos bytes. Puede usar unsigned char en lugar de char si lo prefiere.

Esto no garantiza falsos positivos: en C++ 03, los punteros a las funciones miembro son POD, lo que significa, entre otras cosas, que se pueden copiar utilizando memcpy. Esto implica que si tienen los mismos valores de byte por byte, entonces son los mismos.

El problema es que la representación de almacenamiento de los punteros de función de miembro podría incluir bits que no participan en el valor, por lo que no necesariamente serán los mismos para diferentes punteros a la misma función de miembro. O el compilador podría, por alguna razón oscura, tener más de una forma de señalar la misma función de la misma clase, que no son iguales en bytes. De cualquier manera, puede obtener falsos negativos. Deberá analizar cómo los punteros de función de miembro realmente funcionan en su implementación. Debe implementar operator== para punteros de función de miembro de alguna manera, y si puede averiguar cómo, entonces probablemente pueda averiguar una orden y una función de hash.

Eso es potencialmente difícil: los punteros a las funciones de los miembros son incómodos, y es probable que el almacenamiento incluya diferentes cantidades de "espacio libre" no participante según el tipo de función señalada (virtual, heredada). Por lo tanto, probablemente deba interactuar bastante significativamente con los detalles de implementación de su compilador. Este artículo podría ayudarlo a comenzar: http://www.codeproject.com/KB/cpp/FastDelegate.aspx

Una alternativa más limpia podría ser hacer una búsqueda lineal a través de una matriz para "canonicalizar" todos los punteros a su función, luego compararla y basarla según la posición del "canónico" instancia de ese puntero de función en su matriz. Depende de cuáles son sus requisitos de rendimiento. E incluso si hay requisitos, ¿tiene la clase (y sus clases derivadas) tantas funciones que la búsqueda lineal tardará tanto?

typedef bool (Class::*func)(); 
vector<func> canon; 

size_t getIndexOf(func fn_ptr) { 
    vector<func>::iterator it = find(canon.begin(), canon.end(), fn_ptr); 
    if (it != canon.end()) return it - canon.begin(); 
    canon.push_back(func); 
    return canon.size() - 1; 
} 
+0

¡Gracias el char * hace el truco! Solo en mi compilador necesito un reinterpret_cast en lugar de static_cast. – AllDayCpp

+1

Excelente tratamiento de algunos de los problemas más espinosos, +1. No se me había ocurrido que pmf1 == pmf2 no ​​implica necesariamente una identidad bit a bit. –

+0

Un puntero a la función miembro puede contener relleno, que se ignoraría al comparar por igualdad, y podría tomar valores aleatorios. Al hacer hash cualquier byte de relleno, la función hash fallará. –

0

no podía emitir el puntero (en Microsoft compilador 2010) como se describe en la respuesta anterior, pero esto funciona para mí:

static string fmptostr(int atype::*opt) 
    { 
     char buf[sizeof(opt)]; 
     memcpy(&buf,&opt,sizeof(opt)); 
     return string(buf,sizeof(opt)); 
    } 

Acerca de identidad a nivel de bits del puntero, se puede bit a bit por lo que parece si se usan los modificadores de compilador apropiados. Al menos esto es cierto para el compilador de Microsoft E.g usando #pragma pointers_to_members y un modificador .../vmg

Cuestiones relacionadas