Ich möchte, dass meine Funktionen entweder eine Erfolgsmeldung oder ein Objekt zurückgeben, das die Art des Fehlers beschreibt. Ich würde normalerweise Ausnahmen dafür verwenden, aber mir wurde gesagt, sie nicht für allgemeine Codepfade zu verwenden, und dieser Satz von Funktionen kann aus verschiedenen Gründen ziemlich oft scheitern.Wie gibt man Erfolg oder ein Fehlerobjekt von Funktionen zurück?
Mein Gedanke ist, C++ 17's std::optional
zu verwenden, da ich kein vollständiges Fehlerobjekt zurückgeben muss, wenn es nicht benötigt wird. Also mit optional, wenn die Funktion nicht erfolgreich ist, gibt es das Fehlerobjekt zurück, sonst ist das optionale leer. Das Problem damit ist, dass es die Erwartung eines zurückgegebenen Werts umkehrt (d. H. true
zeigt normalerweise Erfolg, nicht Fehler an).
Ich konnte Leute bekommen eine is_success
Funktion zu verwenden, die wie folgt verwendet werden würde, Klasse mein Fehler Error
vorausgesetzt:
auto result = do_stuff();
if (!is_success(result)) {
Error err = *result;
// ...
}
Oder ein Ergebnis Klasse wäre robuster?
class MaybeError {
std::optional<Error> _error;
public:
MaybeError(const Error& error) : _error(error) {}
constexpr MaybeError() : _error({}) {}
explicit operator bool const() {
return !(_error.operator bool());
}
constexpr bool has_error() const {
return !(_error.has_value());
}
constexpr Error& error() & { return _error.value(); }
constexpr const Error & error() const & { return _error.value(); }
constexpr Error&& error() && { return std::move(_error.value()); }
constexpr const Error&& error() const && { return std::move(_error.value()); }
constexpr const Error* operator->() const { return _error.operator->(); }
constexpr Error* operator->() { return _error.operator->(); }
constexpr const Error& operator*() const& { return _error.operator*(); }
constexpr Error& operator*() & { return _error.operator*(); }
constexpr const Error&& operator*() const&& { return std::move(_error.operator*()); }
constexpr Error&& operator*() && { return std::move(_error.operator*()); }
};
, die ähnlich dem ersten Beispiel verwendet werden können:
auto result = do_stuff();
if (!result) {
Error err = *result;
// ...
}
Was ist die beste Option? Gehe ich das richtig? gelingt
bearbeiten Um klar zu sein, die do_stuff
Funktion, die, wenn sie nicht zurückgibt ein Objekt genannt wird. Wenn es immer ohne Fehler gelingen würde, wäre es einfach void do_stuff()
.
Edit 2 In den Kommentaren schlägt Christian Hackl eine weniger over-engineered Lösung, die eine einfache Struktur verwendet wird.
struct MaybeError {
std::optional<Error> error;
};
Dies würde sowohl einfacher sein und meine Sorge sprechen, dass die Menschen Funktionen wahr zurückzukehren erwarten würden, wenn erfolgreich, indem es ausdrücklich, dass es der Fehler machen, die für die getestet wird. Zum Beispiel:
auto result = do_stuff();
if (result.error) {
Error e = *t.error;
// ...
}
Dies scheint bemerkenswert kompliziert, wenn die Verwendung von Ausnahmen viel sauberer und einfacher wäre und weniger Code benötigt. –
Ich würde gerne Ausnahmen verwenden, aber leider hat mein Chef starke Gefühle zu diesem Thema. – ChrisD
Er ist ein Idiot. Ausnahmen sind in gutem C++ - Code nicht optional. –