2012-03-28 19 views
6

Nachdem sich herausgestellt hat, dass das, was ich ursprünglich wollte, wahrscheinlich nicht ohne C++ 11 möglich ist, möchte ich die Anforderung leicht ändern und frage, ob dies erreicht werden kann .Kompilierzeit prüfen, ob eine Basisklasse "interface" ist

previous question

Grundsätzlich möchte ich in der Kompilierung überprüfen, ob eine Klasse von „Schnittstelle“ erbt. Mit Schnittstelle meine ich Klasse nur mit rein virtuellen Methoden. Ich möchte den folgenden Code tun:

template <typename T> 
class Impl : public T { 
public: 
STATIC_ASSERT_INTERFACE(T); 
}; 

Das Verhalten hier ist, wenn T nur rein virtuelle Methoden hat, dann wird es kompilieren und wenn eine seiner Methoden wird nicht scheitern dann.

Kann jemand an so etwas denken?

+1

Wirklich alle Methoden rein virtuell? Sogar der Destruktor? – Andrzej

+0

Vermutlich würden Sie prüfen wollen, ob 'T' auch Basisklassen hat und, wenn ja, ob es sich um" Interfaces "handelt. Aber die Antwort ist in der Tat "Nein". – MSalters

Antwort

2

Dies ist im Grunde vergleichbar mit Java-Schnittstellen. In C++ gibt es keine interface als solche, es ist nur eine Terminologie für eine class mit allen rein-virtuellen Methoden und nur static const Datenelemente verwendet.

Darüber hinaus können reine virtuelle Methoden einen Funktionskörper haben oder nicht. Daher sind rein virtuelle C++ - Methoden nicht genau dieselben wie die abstrakten Methoden von Java.

Leider ist nicht möglich in C++ zu simulieren.

+0

Grundsätzlich möchte ich, dass alle Methoden virtuell sind, um sicherzustellen, dass ich sie überschreibe (ich spreche nicht über C'tor etc ....). –

+0

@VadimS .: Meinst du, du willst sicherstellen, dass du alle Methoden überschreibst, oder willst du sicher gehen, dass wenn du eine Methode schreibst, die eine Basisklassenmethode überschreiben soll? –

+0

Ich möchte (in der Kompilierzeit) einen Fall vermeiden, den ich eine Methode in der Basisklasse überschreiben möchte, aber die Methode wird nicht als 'virtuell' definiert. –

1

Zunächst einmal sind Schnittstellen nicht wirklich ein natives Konzept zu C++. Ich bin sicher, dass die meisten Programmierer wissen, was sie sind, aber der Compiler nicht, und das ist, wo Sie in Probleme geraten. C++ kann eine Menge Dinge tun, und ich wette, Sie können es in viele verschiedene Sprachen verwandeln, aber wenn Sie C++ schreiben, ist es am besten, Dinge auf C++ - Art zu tun.

Eine andere Sache - es gibt eine Menge Grauzone hier. Was passiert, wenn Sie eine „Schnittstelle“ hatte, wie Sie vorgeschlagen, aber jemand hat eine von ihnen:

// Technically not a member function, but still changes the behavior of that class. 
bool operator==(const Interface &left, const Interface &right); 

Ich bin fast 100% sicher, dass Sie nicht jemand zu tun, dass stoppen.

Sie können möglicherweise sicherstellen, dass es keine Mitgliedsvariablen gibt, obwohl ich nicht sicher bin, ob ich dieser Vorgehensweise zustimme. Erstellen Sie eine leere Klasse, und führen Sie dann eine static_assert(sizeof(InterfaceClass) == sizeof(Empty)). Ich bin nicht sicher, ob es sicher ist anzunehmen, dass die Größe 0 wäre - das ist eine Frage für jemanden, der mit den Standards vertrauter ist.

+1

Es wird niemals 0 sein. Wenn Sie mehrere Variablen vom Typ 'Empty' deklarieren, muss jede von ihnen eine eindeutige Adresse im Speicher haben. – enobayram

+1

'static_assert (sizeof (InterfaceClass) == sizeof (Leer))' - kann nicht sicher sein. Größe der und leere Klasse (ohne Mitglied Variablen und keine virtuellen Funktionen) wird 1 Byte und die Größe einer 'InterfaceClass' wird in der Regel nicht 1 Byte, höchstwahrscheinlich würde es virtuelle Funktion, die die Größe 4 Bytes machen (Größe von a Zeiger) – Sanish

1

Was Sie wollen, kann nicht direkt getan werden, wie andere bereits erläutert haben.

Allerdings können Sie das Verhalten, das Sie wollen, mit ein wenig Disziplin von den Schnittstellenentwicklern erhalten. Wenn alle Ihre Schnittstellen von einer gemeinsamen Basisklasse Interface abstammen, können Sie überprüfen, ob eine Basisklasse zum Zeitpunkt der Kompilierung ist. Verwenden Sie dazu eine ähnliche Methode wie this question.

Zum Beispiel:

class Interface { 
    public : 
     virtual ~Interface() { } 
}; 

template <typename T> 
struct IsDerivedFromInterface { 
    static T t(); 
    static char check(const Interface&); 
    static char (&check(...))[2]; 
    enum { valid = (sizeof(check(t())) == 1) }; 
}; 

class MyInterface : public Interface { 
    public : 
     virtual void foo() = 0; 
}; 

class MyBase { 
    public : 
     virtual void bar() { } 
}; 

class Foo : public MyInterface { 
    public : 
     virtual void foo() { } 
}; 
BOOST_STATIC_ASSERT(IsDerivedFromInterface<Foo>::valid); // just fine 

class Bar : public MyBase { 
    public : 
     virtual void bar() { } 
}; 
BOOST_STATIC_ASSERT(IsDerivedFromInterface<Bar>::valid); // oops 

Natürlich kann der Entwickler der Basisklasse betrügen und leiten sich von Interface obwohl die Basisklasse nicht eine Schnittstelle ist. Deshalb sagte ich, dass es etwas Disziplin vom Entwickler erfordert.

Das sagte jedoch, ich kann nicht sehen, wie das nützlich wäre. Ich habe nie gedacht, dass ich diese Art von Kompilierzeitprüfung benötige.