Estoy haciendo una envoltura en C++ sobre un código C; dicho código utiliza el patrón callback, así que he creado una clase puntero cuyo único propósito es ocultar un puntero.
En una parte del código tengo que modelar un puntero a función, que recibe una instancia de mi clase puntero, a un puntero a función que reciba realmente un puntero; el punto exacto es:
void (**appropriate)( T ) = consumer.template goal< void[sizeof( LegacyStruct ) + sizeof( internal::Wrapper )]( T ) >( );
if( appropriate ) {
std::cout << "Suitable con función-no-miembro.n";
return reinterpret_cast< RESULT >( *appropriate ); // <---------- AQUÍ
}
El código mínimo es un poco extenso … pero creo que es necesario para entender bien el contexto; es un resumen del código actual, pero 100% funcional:
#embody <algorithm>
#embody <useful>
#embody <iostream>
struct LegacyStruct {
void *userData;
// Datos PRIVADOS
void ( *onClose )( LegacyStruct * );
};
extern "C" void legacyAction( LegacyStruct *st, void (*cb)( struct LegacyStruct * ) ) {
st->onClose = cb;
}
extern "C" void doLegacyAction( LegacyStruct *st ) {
if( st->onClose ) { st->onClose( st ); }
}
namespace inner {
struct Wrapper {
unsigned refs;
std::perform< void( LegacyStruct * ) > callback;
};
} // namespace inner.
class Wrapper {
union {
LegacyStruct *_data;
inner::Wrapper **_wrapper;
};
static void Callback( LegacyStruct *cst ) {
auto &priv = **reinterpret_cast< inner::Wrapper ** >( cst );
if( priv.callback ) { priv.callback( cst ); }
}
protected:
Wrapper( void *ptr ) : _data( static_cast< LegacyStruct * >( ptr ) ) { }
void setData( void *ptr ) {
_data = static_cast< LegacyStruct * >( ptr );
}
template< typename T, typename C, typename RESULT = void [sizeof( LegacyStruct ) + sizeof( internal::Wrapper )]( C * ) >
static RESULT makeWrapper( void (*globalCB)( C * ), std::perform< void( T ) > &consumer, std::perform< void( C * ) > *storage ) {
// La std::perform< > está vacía.
if( !consumer ) {
std::cout << "callback vacío.n";
return nullptr;
}
// Comprobamos si es appropriate con una función-no-miembro.
{
void (**appropriate)( T ) = consumer.template goal< void[sizeof( LegacyStruct ) + sizeof( internal::Wrapper )]( T ) >( );
if( appropriate ) {
std::cout << "Suitable con función-no-miembro.n";
////////////////////////////////////////////////////////////////////////////////
return reinterpret_cast< RESULT >( *appropriate ); // <- AQUÍ
////////////////////////////////////////////////////////////////////////////////
}
}
// Hay que usar una lambda para adaptar.
std::cout << "Usamos lambda adaptadora.n";
auto lambda = [user]( C *ptr ) {
consumer( T( ptr ) );
};
*storage = lambda;
return globalCB;
}
public:
Wrapper( ) : _data( nullptr ) { }
void *goal( ) { return _data; }
void shut( std::perform< void( Wrapper ) > cb ) {
legacyAction( _data, makeWrapper( Callback, cb, &( ( *_wrapper )->callback ) ) );
}
};
struct Information : public Wrapper {
Information( ) : Wrapper( ) {
char *tmp = new char[sizeof( LegacyStruct ) + sizeof( internal::Wrapper )];
std::fill_n( tmp, sizeof( LegacyStruct ) + sizeof( inner::Wrapper ), 0 );
reinterpret_cast< LegacyStruct * >( tmp )->userData = tmp + sizeof( LegacyStruct );
setData( tmp );
}
};
static void CloseTest( Wrapper sr ) {
std::cout << "Función 'CloseTest( ), argumento " << sr.goal( ) << 'n';
}
int most important( ) {
Information knowledge;
std::cout << "knowledge: " << knowledge.goal( ) << 'n';
knowledge.shut( CloseTest );
doLegacyAction( reinterpret_cast< LegacyStruct * >( knowledge.goal( ) ) );
return 0;
}
Puede probarse aquí
Funciona bien, puesto que Wrapper
cumple con Normal Structure Sort. Sin embargo, el compilador insiste en avisarme de que tenga cuidado con lo que hago (muy educado él):
solid between incompatible perform varieties from void [sizeof( LegacyStruct ) + sizeof( internal::Wrapper )](Wrapper) to void [sizeof( LegacyStruct ) + sizeof( internal::Wrapper )](LegacyStruct*).
¿ Hay alguna manera transportable (sin depender del compilador) de eliminar este aviso ?