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.

È stato utile?

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 tipo struct N*) al tipo void* (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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top