Puntatore all'inizio di un oggetto (C ++)
Domanda
Ho bisogno di un modo per ottenere un puntatore all'inizio di un oggetto in C ++. Questo oggetto viene utilizzato all'interno di un modello in modo che possa essere di qualsiasi tipo (polimorfi o meno) e potrebbe essere un oggetto che utilizza l'ereditarietà multipla.
questo articolo che descrive un modo per farlo (vedere la sezione chiamato "calchi dinamici") utilizzando typeid e un dynamic_cast a * vuoto nel caso in cui T è un tipo polimorfico.
Questo funziona perfettamente su MSVC, tuttavia il GCC (4.x) sembra cadere sul suo culo e sputa fuori un errore di compilazione quando viene utilizzato con un tipo non polimorfico.
Qualcuno sa un modo per:
- Fai GCC si comportano in sé, e valuta TypeId correttamente
- O un altro modo per fare questo, che compilerà il GCC
Di seguito è riportato il codice Attualmente sto usando per cercare di raggiungere questo obiettivo.
template <typename T>
void* dynamicCastToVoidPtr(T *const ptr)
{
// This is done using a separate function to avoid a compiler error on some
// compilers about non-polymorphic types when calling startOfObject
return dynamic_cast<void*>(ptr);
}
template <typename T>
void* startOfObject(T *const ptr)
{
// In cases of multiple inheritance, a pointer may point to an offset within
// another object
// This code uses a dynamic_cast to a void* to ensure that the pointer value
// is the start of an object and not some offset within an object
void *start = static_cast<void*>(ptr);
if(start)
typeid(start = dynamicCastToVoidPtr(ptr), *ptr);
return start;
}
template <typename T>
void doSomethingWithInstance(T *const instance)
{
// Here is where I need to get a void* to the start of the object
// You can think of this as the deleteInstance function of my memory pool
// where the void* passed into freeMemory should point to the
// start of the memory that the memory pool returned previously
void *start = startOfObject(instance);
if(start)
allocator->freeMemory(start);
}
Grazie.
Soluzione 3
ho trovato una soluzione da un'altra domanda che mi permette di lavoro out al momento della compilazione, se un tipo è polimorfa e posso quindi utilizzare questo modello con la specializzazione di utilizzare il tipo corretto di cast. A quanto pare questo metodo potrebbe rompersi se il compilatore aggiunge imbottitura tra i sub-oggetti, ma posso spera aggiungere un po 'di tempo di compilazione afferma in alcuni casi noti per la cattura di questo. Lo fa compilare ed eseguire correttamente su entrambi MSVC e GCC.
Questo è il codice per capire se un tipo è polimorfico.
#define SIMPLE_POLYMORPHIC(TYPE, POLYMORPHIC) \
template <> \
struct IsPolymorphic<TYPE> \
{ \
static const bool value = POLYMORPHIC; \
};
template <typename T>
struct IsPolymorphic
{
struct Derived : public T { virtual ~Derived(); };
static const bool value = (sizeof(Derived) == sizeof(T));
};
SIMPLE_POLYMORPHIC(int, false);
SIMPLE_POLYMORPHIC(unsigned int, false);
// ... do this for all intrinsic or non-derivable types
Il codice per eseguire il casting a seconda che il tipo è polimorfico.
template <typename T, bool isPolymorphic = IsPolymorphic<T>::value>
struct StartOfObject
{
static void* getStart(T *const ptr)
{
return static_cast<void*>(ptr);
}
};
template <typename T>
struct StartOfObject<T, true>
{
static void* getStart(T *const ptr)
{
if(ptr)
return dynamic_cast<void*>(ptr);
return NULL;
}
};
E un banco di prova per esso.
#define CLASS_STUFF(CLASS) \
public: \
CLASS() {} \
virtual ~CLASS() {} \
int m_##CLASS;
class A
{
CLASS_STUFF(A);
};
class B : public A
{
CLASS_STUFF(B);
};
class C
{
};
#include <iostream>
int main()
{
std::cout << IsPolymorphic<A>::value << std::endl;
std::cout << IsPolymorphic<B>::value << std::endl;
std::cout << IsPolymorphic<C>::value << std::endl;
std::cout << IsPolymorphic<int>::value << std::endl;
StartOfObject<A>::getStart(new A());
StartOfObject<B>::getStart(new B());
StartOfObject<C>::getStart(new C());
StartOfObject<int>::getStart(new int());
return 0;
};
Altri suggerimenti
Il messaggio di errore esatto da gcc è
Errore : non può dynamic_cast
&n
(di tipostruct N*
) al tipovoid*
(tipo sorgente non è polimorfica)
Questo potrebbe essere gestita tramite boost::is_polymorphic
in combinazione con boost::enable_if
e boost::disable_if
, induttanze purtroppo gcc con l'approccio più ovvio, ecco la soluzione:
template <class T>
void* address_of_impl(T* p, boost::enable_if< boost::is_polymorphic<T>, int >)
{
return dynamic_cast<void*>(p);
}
template <class T>
void* address_of_impl(T* p, ...) { return static_cast<void*>(p); }
template <class T>
void* address_of(T* p) { return address_of_impl(p, 0); }
Dove usiamo SFINAE a nostro vantaggio (i puntini di sospensione è sempre considerato ultimo nella risoluzione di sovraccarico in modo che il compilatore primi tentativi di utilizzare la versione dynamic_cast
che non riesce per i tipi non polimorfici a causa della enable_if
).
Non ho prova su gcc 3.4 e passò. Sto indagando in un'altra domanda perchè usando disable_if
invece di ...
non funziona.
Modifica :
ed è stato un semplice errore di battitura (dimenticato il bit ::type
):
template <class T>
typename boost::enable_if< boost::is_polymorphic<T>, void* >::type
address_of(T* p) { return dynamic_cast<void*>(p); }
template <class T>
typename boost::disable_if< boost::is_polymorphic<T>, void* >::type
address_of(T* p) { return static_cast<void*>(p); }
Ho fatto qualche indagine, e possono hanno trovato un bug nel GCC qui; Vorrei segnalarlo . Tuttavia, ci può essere qualche regola non riesco a trovare che dice che la funzione di modello sul lato sinistro l'operatore virgola ha bisogno di essere istanziati in questo caso, anche se l'espressione complessiva non viene valutata; in questo caso la tecnica in questo articolo è semplicemente sbagliato.
Vi suggerisco di vedere se boost :: :: type_traits is_polymorphic può essere fatto per il lavoro per voi.