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.
Warum overcomplicating Sie die Dinge. Warum nicht einfach 'ParentOne * p1 = Kind' verwenden? –
Ihr Programm sieht für mich vollkommen OK aus. Welche Compiler und Flags verwenden Sie? –
@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. –