2016-03-31 12 views
0

Eine Bibliothek von Drittanbietern, die ich verwende, erfordert, dass Zeiger als void* übergeben werden. Zu jedem gegebenen Zeitpunkt muss dieser Zeiger möglicherweise in eine von mehreren Klassen in der Vererbungskette umgewandelt werden. Dies schlägt fehl, wenn eine Mehrfachvererbung involviert ist. Meine erfundene Methode, um dies sicher durchzuführen, besteht darin, den Klassentyp zusammen mit dem Zeiger zu propagieren, um den gewünschten Typ korrekt zu erzeugen. Im Folgenden zeigt das beabsichtigte Ziel:Verwenden Sie Vorlagenfunktion, um zwischen mehreren Klassen sicher zu übertragen

#include <assert.h> 
#include <stdint.h> 
#include <cstddef> 
#include <stdio.h> 

enum class kTypeFlag { PARENTONE, PARENTTWO, CHILD }; 

#define CAST_TO_CLASS_TYPE(TO, FROM, POINTER)       \ 
    dynamic_cast<TO*>(static_cast<FROM*>(POINTER)) 

class ParentOne { 
public: 
    ParentOne() { } 
    virtual ~ParentOne() { } 
    virtual size_t byte_size() const = 0; 
}; 

class ParentTwo { 
public: 
    ParentTwo(uint32_t id) : id_(id) { } 
    virtual ~ParentTwo() { } 
    uint32_t id() const { return id_; } 
private: 
    const uint32_t id_; 
}; 

class Child : public ParentOne, public ParentTwo { 
public: 
    Child(uint32_t id) : ParentOne(), ParentTwo(id) { } 
    virtual ~Child() { } 
    size_t byte_size() const override { return sizeof(*this); } 
}; 

template <class T> 
T* Convert(void* pointer, kTypeFlag flag) { 
    switch (flag) { 
    case kTypeFlag::PARENTONE: 
     return CAST_TO_CLASS_TYPE(T, ParentOne, pointer); 
    case kTypeFlag::PARENTTWO: 
     return CAST_TO_CLASS_TYPE(T, ParentTwo, pointer); 
    case kTypeFlag::CHILD: 
     return CAST_TO_CLASS_TYPE(T, Child, pointer); 
    default: 
     assert(0 && "invalid flag to Convert"); 
    } 
} 

int main() { 
    Child* child = new Child(5); 
    printf("byte_size: %zu\n", child->byte_size()); 
    printf("id: %u\n", child->id()); 

    ParentOne* p1 = Convert<ParentOne>(child, kTypeFlag::CHILD); 
    ParentTwo* p2 = Convert<ParentTwo>(child, kTypeFlag::CHILD); 
    printf("byte_size: %zu\n", p1->byte_size()); 
    printf("id: %u\n", p2->id()); 
} 

Beachten Sie, dass CAST_TO_CLASS_TYPE ein dynamic_cast verwendet anstelle eines static_cast. Dies liegt daran, wenn ein static_cast verwendet wird, die Build mit fehlschlägt:

run.cc:40:14: error: static_cast from 'ParentTwo *' to 'ParentOne *', which are not related by inheritance, is not allowed 
     return CAST_TO_CLASS_TYPE(T, ParentTwo, pointer); 
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
run.cc:9:3: note: expanded from macro 'CAST_TO_CLASS_TYPE' 
    static_cast<TO*>(static_cast<FROM*>(POINTER)) 
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
run.cc:53:19: note: in instantiation of function template specialization 'Convert<ParentOne>' requested here 
    ParentOne* p1 = Convert<ParentOne>(child, kTypeFlag::CHILD); 
       ^

Also meine Fragen sind: Warum wird die Build fehlschlagen, wenn static_cast mit? Oder gibt es einen anderen Weg, dies vollständig zu tun? Das Endziel ist es, eine Art Sicherheit zu erzwingen, um ein void* auf mehrere verschiedene Arten zu werfen. Ich bin nicht besorgt darüber, wie das gemacht wird. Das obige ist einfach das Beste, was ich mir vorstellen kann.

BEARBEITEN: Erlauben Sie mir zu erklären, warum dynamic_cast nicht ideal ist. Das folgende Snippet kompiliert:

ParentTwo* p2 = new ParentTwo(42); 
ParentOne* p1 = Convert<ParentOne>(p2, kTypeFlag::PARENTTWO); 
printf("id: %u\n", p2->id()); 
printf("byte_size: %zu\n", p1->byte_size()); 

Aber es bricht zur Laufzeit ab. Vorzugsweise würde dies zur Kompilierzeit abgefangen werden. Aus diesem Grund möchte ich eine static_cast verwenden, ist aber aus dem oben genannten Grund nicht möglich.

+0

Warum overcomplicating Sie die Dinge. Warum nicht einfach 'ParentOne * p1 = Kind' verwenden? –

+0

Ihr Programm sieht für mich vollkommen OK aus. Welche Compiler und Flags verwenden Sie? –

+0

@MohitJain Dieser Code dient nur zur Demonstration. Bei der Verwendung von 'child' wurde ein' void * 'zugewiesen, wo ich nur auf die Elternklasse zugreifen möchte. –

Antwort

2

Sie haben bereits eine Erklärung erhalten, warum Ihr Code in den Kommentaren nicht funktioniert. Du scheinst eine Alternative zu haben, also hier ist es.

http://ideone.com/24iaNv

namespace details 
{ 
    template<typename To, kTypeFlag> 
    struct cex {To* operator()() { static_assert(!std::is_same<To, To>::value, "invalid flag to Convert");} }; 
    template<typename To> 
    struct cex<To, kTypeFlag::PARENTONE> { To* operator()(void*p) { return static_cast<To*>(static_cast<ParentOne*>(p)); } }; 
    template<typename To> 
    struct cex<To, kTypeFlag::PARENTTWO> { To* operator()(void*p) { return static_cast<To*>(static_cast<ParentTwo*>(p)); } }; 
    template<typename To> 
    struct cex<To, kTypeFlag::CHILD>  { To* operator()(void*p) { return static_cast<To*>(static_cast<Child*>(p)); } }; 
}; 

template <typename T, kTypeFlag f> 
T* ConvertEx (void* pointer) { return details::cex<T, f>()(pointer); } 
+0

Am ausgezeichnetesten. Dies erzeugt genau die Ergebnisse, auf die ich gehofft hatte. –

+0

Um 'static_assert' zuzulassen, müssen Sie die Bedingung vom Template-Parameter abhängig machen, indem Sie' false' durch '! Std :: is_same :: value' ersetzen. – Jarod42

+0

@ Jarod42 Danke, ich benutze kein GCC, also hatte ich keine Ahnung. Weißt du zufällig wer hier ist, Visual Studio oder GCC? – Jts

Verwandte Themen