enum vs struct
-
hola leute
mir ist leider kein besserer titel eingefallen.
ich will in einer basisklasse ein paar fehlernummern definieren (no_error, still_exists, not_found...).
nach dem jedoch in jeder abgeleiteten klasse neue fehler hinzu kommen koennen und man ein enum nicht nachtraeglich erweitern kann, hab ich die 'fehler' mal in ein struct zusammen gefasst.struct Error { const static int no_error = 0x00; const static int not_found = 0x01; const static int still_exists = 0x02; ... }somit kann ich in abgeleiteten klassen neue fehlerarten nachtraeglich hinzufuegen. irgendwie gefaellt mir diese loesung jedoch nocht besonders. kennt jemand von euch was besseres? wie wuerdet ihr das problem angehen?
Meep Meep
-
Ein struct nur aus statischen Konstanten halte ich für eher sinnlos - dann nimm doch lieber einen namespace

-
re
meine struct ist aber in der basisklasse definiert. das wird dann mit namespace wohl eher nicht funktionieren.
Meep Meep
-
Warum sollte das nicht funktionieren?
namespace error_numbers { const static int no_error = 0x00; const static int not_found = 0x01; const static int still_exists = 0x02; } class A { using namespace error_numbers; };Das einzige Problem hier ist, dass die abgeleiteten Klassen 1. den Namespace angeben müssen und 2. aufpassen, dass sie keine Konstanten hinzufügen, die andere Variablen überlappen.
Edit: Oder meintest du, dass du die Basisklasse nicht bearbeiten kannst?
-
re
viande schrieb:
Edit: Oder meintest du, dass du die Basisklasse nicht bearbeiten kannst?
richtig
vor allem will ich das jede klasse ihre eigene fehlerdefinition mitbringt. und das sollte dann immer wie folgt geschrieben werden koennen:
my_class::Error::no_errorist zwar etwas mehr schreibarbeit jedoch finde ich es so derzeit am uebersichtlichsten.
Meep Meep
-
Hallo,
ich finde die Lösung gut. Ich habe (in .NET) bereits etwas ähnliches gemacht, weil mir auch dort die Möglichkeiten der konventionellen Enums nicht ausreichten. Allerdings sah meine Lösung in eher folgendermaßen aus, um streng typisierte Werte zu erhalten:
struct my_enum { static my_enum const value1; static my_enum const value2; bool operator ==(my_enum const& other) { return m_value == other.m_value; } bool operator !=(my_enum const& other) { return m_value != other.m_value; } protected: my_enum(int value) : m_value(value) { } private: int m_value; }; my_enum const my_enum::value1 = 1; my_enum const my_enum::value2 = 2;In C++ ist das natürlich problematisch, weil man es jetzt nicht in 'switch'-Statements verwenden könnte. Eventuell hilft hier eine Konversion nach 'int' ab.
/EDIT: Mit Flag-artigen Enums funktioniert das so natürlich nicht mehr, es sei denn, man definiert noch weitere Operatoren.
-
Dir ist hoffentlich klar, daß du die Error-Klasse genausowenig erweitern kannst wie einen enum. Und außerdem solltest du noch irgendwie sicherstellen, daß Fehlernummern nicht doppelt vergeben werden.
-
re
das mit den mehrfach vorkommenden fehlernummern ist natuerlich ein problem das ich nicht unterschaetzen darf.
vielleicht kann man es auch folgend irgendwie machen:
//pseudocode struct Error { enum {err1, err2, err3, ..., err_tail}; } struct ExtendedError : public Error { enum {Error::err_tail, next_err1, next_err2, ..., err_tail}; }ginge das irgendwie ?
Meep Meep
-
re
struct err1 { enum {e1,e2,e3,e_tail}; }; struct err2 : public err1 { enum { e_head = err1::e_tail, e4,e5,e6, e_tail}; };so funktioniert das sogar wie gewuenscht. muss halt nach jeder 'ableitung' das
e_head = err1::e_taileinbauen und am schluss wieder ein
e_taileinfuegen
aber dafuer kann es nix mehr doppeltes geben
Meep Meep
-
Hi,
das ist ja eine pfiffige Idee (bin ich nicht drauf gekommen) ... allerdings sehe ich eine "Unschönheit": e_tail ist jeweils ein gültiger Wert - soll er das sein ?
Entweder musst Du ihn also auch fachlich nutzen (und dann in der ablitenden Klasse wissen, dass gerade DER der letzte sein soll) - oder Du "lagerst ihn aus":struct err1 { enum {e1,e2,e3,e4}; unsigned int const e_tail = e4; // jetzt hast Du hier aber "Inkonsistenzrisiko" }; ...Weiß jetzt auch nicht, was besser ist.
Gruß,
Simon2.
-
hoi
Simon2 schrieb:
allerdings sehe ich eine "Unschönheit": e_tail ist jeweils ein gültiger Wert - soll er das sein ?
das koennte zu einem prolem werden wenn ich meine fehlernummern durchgaengig haben will. die fehlercodes sind mir nummerisch jedoch egal. ansonsten muesste ich dem ersten wert schon mal einen definierten wert vorgeben. oder kann ich mich darauf verlassen, das der erste wert eines enums auf jedem compiler immer '0' bekommt?
mir gehts dabei eigendlich nur darum das ich keine magic_numbers verweden muss.
ansonsten wuerde ich e_tail immer am schluss anhaengen und in der ableitung oder erweiterung dann dem ersten enum-wert den e_tail der base zuweisen. dann hab ichs auch durchgehend.
die inkonsistenz hab ich ansich garnicht, weil e_tail eh nicht dazu gehoert, zu den fehlermeldungen.Meep Meep
-
Meep Meep schrieb:
re
struct err1 { enum {e1,e2,e3,e_tail}; }; struct err2 : public err1 { enum { e_head = err1::e_tail, e4,e5,e6, e_tail}; };Solange du so diszipliniert bist, diese Folge systematisch durchzuhalten, klappt das sicher recht gut. Aber sobald du eine weitere Klasse von err1 ableitest, hast du trotzdem wieder doppelte Fehlernummern.
-
re
eigendlich auch ein bloedsinn das mit dem e_head. ich definiere fuer mich einfach ein keyword 'enum_end' das ich in jedem enum, das erweiterbar sein soll, am schluss angebe. und bei einer erweiterung einfach das erste element dem 'base::enum_end' zuweise.
funktioniert ja dann aehnlich von der logik her, wie bei nem z.b. vector mit dem 'vector.end()'. das kann ich mir dann sogar noch merken ;o)Meep Meep
-
re
[/quote]Solange du so diszipliniert bist, diese Folge systematisch durchzuhalten, klappt das sicher recht gut. Aber sobald du eine weitere Klasse von err1 ableitest, hast du trotzdem wieder doppelte Fehlernummern.[/quote]
wieso denn das ? in dem fall waeren die zwei abgeleiteten klassen ja auch unterschiedlich. die zwei 'erweiterbaren enums' waeren ja nicht die selben. sie haben doch nur die gleichen elemente des urspruenglichen enums.
Meep Meep
-
Ja, die beiden Klassen haben jeweils einen eigenen enum mit eigenen Bezeichnern, aber die Werte dahinter sind identisch:
struct errb : public err1 { enum{e_head=err1::e_tail,eb4,...} };^^ jetzt werden dein err2::e4 und errb::eb4 durch den selben Wert dargestellt - das heißt, die beiden Fehlernummern wirst du später nicht mehr unterscheiden können.
-
Meep Meep schrieb:
CStoll schrieb:
Solange du so diszipliniert bist, diese Folge systematisch durchzuhalten, klappt das sicher recht gut. Aber sobald du eine weitere Klasse von err1 ableitest, hast du trotzdem wieder doppelte Fehlernummern.
wieso denn das ? in dem fall waeren die zwei abgeleiteten klassen ja auch unterschiedlich. die zwei 'erweiterbaren enums' waeren ja nicht die selben. sie haben doch nur die gleichen elemente des urspruenglichen enums.
Meep Meep
Oh doch:
//pseudocode struct Error { enum {err1, err2, err3, err_tail}; } // -> Error::err1 == 0, Error::err2 == 1, Error::err3 == 2, Error::err_tail == 3 struct ExtendedError1 : public Error { enum {next_err1 = Error::err_tail, next_err2, err_tail}; } // -> ExtendenError1::next_err1 == 3, ExtendedError1::err2 == 4, ExtendedError1::err_tail == 5 struct ExtendedError2 : public Error { enum {next_err1 = Error::err_tail, next_err2, err_tail}; } // -> ExtendenError2::next_err1 == 3, ExtendedError2::err2 == 4, ExtendedError2::err_tail == 5Als 3 und 4 wurden doppelt vergeben (ExtendedError1 und ExtendedError2) - und genau das wolltest Du doch mit dem Konstrikt vermeiden, oder ?
Gruß,
Simon2.
-
re
also irgendwie reden wir gerade aneinander vorbei.
ich hab meine basis-enum:struct Error { enum {no_error, not_found, already_exists, enum_end}; }jetzt moechte ich es in 2 unterschiedlichen klassen erweitern:
struct ErrorClass1 : public Error { enum { fehler1 = Error::enum_end, fehler2, fehler3}; } struct ErrorClass2 : public Error { enum { fehler1 = Error::enum_end, fehler2, fehler3}; }die methode 'my_meth' der klasse 'Class1' gibt als fehlermeldung nun ErrorClass1 zurueck und die methode 'my_meth' der klasse 'Class2' gibt als fehler ErrorClass2 zurueck. unterschiedliche Errors jedoch mit bestimmten gleichen base_errors. somit kann mir dann egal sein ob in ErrorClass1 und ErrorClass2 die selben werte vorhanden sind.
ansonsten muss ich halt ErrorClass2 von ErrorClass1 erben lassen.struct ErrorClass2 : public ErrorClass1 { enum {next_error1 = ErrorClass1::enum_end, next_error2, next_error3, enum_end}; }oder ich hab dich nicht richtig verstanden. dann haett ich mir jatz aber das tippen sparen koennen ;o)
Meep Meep
-
enum's (besonders anonyme enum's wie in deinem Code) sind nichts weiter als eine nette Verpackung für int-Werte. Und wenn der Compiler zwei enum-Konstanten den selben Wert gegeben hat, kannst du sie hinterher nicht mehr auseinanderhalten.
ansonsten muss ich halt ErrorClass2 von ErrorClass1 erben lassen.
Ja, das solltest du - aber das wird bei größeren Projekten schnell unübersichtlich (vor allem wenn jede Teil-Bibliothek ihre eigenen Fehlernummern definieren will, mußt du ständig das halbe Projekt recompilieren, nur weil du in der obersten Error-Klasse etwas ergänzt hast).
-
Mal ne andere Frage.
was willst du überhaupt mit errorcodes bezwecken?
Neben den Problemen mit der eindeutigen id musst du auch darauf achten, dass der user am ende auch auf errorcodes prüft. das ist aber ein großes problem. weil viele einfach zur faulheit neigen.
u.a. aus diesen gründen wurde ja auch exceptions eingeführt.
-
ich würde einfach das komplette enum für alle klassen in einen namespace setzen, wozu die rumeierei mit den ableitungen, wen man es auch einfach auslagern kann