2017-01-11 1 views
2

ich eine Basisklasse für Ausnahmen haben:Vermeiden von Makros für nahezu identische Klassendefinitionen

class BaseException : public std::runtime_error 
{ 
public: 

    BaseException(int Code, std::string Msg) : runtime_error(Msg); 

    //...etc 
}; 

In jeder Klasse, die Ausnahmen braucht, binde ich eine Exception Klasse, die von BaseException erbt:

class Foo 
{ 
public: 

    class Exception : public BaseException 
    { 
    public: 

     Exception(int Code, std::string OptMsg = "") : BaseException(Code, OptMsg); 

     enum 
     { 
      Fire, 
      Flood, 
      Aliens 
     }; 
    }; 

    //...etc 
}; 

So Jetzt kann ich throw(Exception::Fire) innerhalb Foo und fangen nach Basisklasse oder durch Foo::Exception& und zum Beispiel mit Foo::Exception::Fire vergleichen.

Die Klassendefinition Exception ist fast immer identisch, nur der Inhalt des Enums ändert sich. Wegen DRY, dachte ich einen Makro zu schreiben, die so etwas wie dies ermöglicht:

EXCEPTIONS 
Fire, 
Flood, 
Aliens 
END_EXCEPTIONS 

Allerdings sind Makros verpönt in C++. Gibt es einen besseren Weg?

+0

Wie lautet die Enumeration für und wie hängt sie mit den Argumenten des Konstruktors zusammen? –

+0

Die Aufzählung beschreibt die Ausnahme beim Werfen/Fangen, fügte diese Information zu der Frage hinzu. – Unimportant

+2

Ich denke, das ist ein Designproblem. Die Information darüber, welche Art von Ausnahmesituation aufgetreten ist, ist unterteilt in * Typ * und * Daten *. Dies scheint inkonsistent zu sein, und es ist nicht typsicher, da Sie jedem Konstruktor "int" und nicht nur gültigen enum-Werten übergeben können. Wenn ich Sie wäre, würde ich mich für einen viel einfacheren Ansatz entscheiden, indem ich entweder den Statuscode oder die BaseException-Unterklassen eliminieren würde. –

Antwort

2

Sie können die BaseException-Konstruktoren mit dem Schlüsselwort using "erben", anstatt sie manuell neu zu implementieren. Dies sollte Ihnen ein wenig Tipparbeit ersparen. Der Rest des Boilerplate ist ziemlich minimal, also würde ich mir persönlich keine Sorgen machen.

struct Foo { 
    struct Exception : BaseException { 
    using BaseException::BaseException; 
    enum { 
     Fire, 
     Flood, 
     Aliens 
    }; 
    }; 
}; 
+0

Wenn Sie es in eine 'struct' verwandeln, dann können Sie auch' public' aus dem Basisklasse-Spezifizierer entfernen. –

+0

Das habe ich verpasst. Ich habe es nur zur Kürze in eine Struktur verwandelt. –

2

Wie wäre:

template <typename T> 
struct Exception : BaseException 
{ 
    Exception(int Code, std::string OptMsg = "") : BaseException(Code, OptMsg); 
}; 

class Foo 
{ 
public: 

    using Exception = ::Exception<Foo>;  
    enum ExceptionCodes 
    { 
     Fire, 
     Flood, 
     Aliens 
    }; 

    //...etc 
}; 

(oben ungesehen durch einen Compiler)

Der einzige Unterschied ist, dass Sie Foo::Fire oder Foo::ExceptionCodes::Fire beziehen haben.

Sie könnten ohne die using Anweisung, und nur unter Exception<Foo>.

+1

* "von einem Compiler nicht zu sehen" * - Deshalb sollte man nicht überrascht sein, wenn die using-Anweisung zu einem Fehler führt. Hast du zufällig ein '=' vergessen? – StoryTeller

+1

@StoryTeller: Und ein Scope-Operator, denke ich.Innerhalb von Foo würde 'Foo :: Exception' versuchen, unqualifiziert nach 'Exception ' zu suchen, was es zu einer zirkulären Definition macht, zusätzlich zu 'Foo :: Exception', das keine Vorlage ist. – MSalters

+0

@StoryTeller Seufzer. Ich hätte es besser wissen sollen, als 'using' zu verwenden und an den guten alten' typedef' zu kleben. –

Verwandte Themen