diff --git a/repos/base/include/util/attempt.h b/repos/base/include/util/attempt.h new file mode 100644 index 0000000000..5aa3eb6e3a --- /dev/null +++ b/repos/base/include/util/attempt.h @@ -0,0 +1,79 @@ +/* + * \brief Utility for passing return values + * \author Norman Feske + * \date 2021-11-09 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _INCLUDE__UTIL__ATTEMPT_H_ +#define _INCLUDE__UTIL__ATTEMPT_H_ + +namespace Genode { template struct Attempt; } + + +/** + * Option type for return values + * + * The 'Attempt' type addresses the C++ limitation to only a single return + * value, which makes it difficult to propagate error information in addition + * to an actual return value from a called function back to the caller. Hence, + * errors have to be propagated as exceptions, results have to be returned via + * out parameters, or error codes have to be encoded in the form of magic + * values. Each of these approaches create its own set of robustness problems. + * + * An 'Attempt' represents the result of a function call that is either a + * meaningful value or an error code, but never both. The result value and + * error are distinct types. To consume the return value of a call, the caller + * needs to specify two functors, one for handing the value if the value exists + * (the call was successful), and one for handling the error value if the call + * failed. Thereby the use of an 'Attempt' return type reinforces the explicit + * handling of all possible error conditions at the caller site. + */ +template +class Genode::Attempt +{ + private: + + bool _ok; + RESULT _result { }; + ERROR _error { }; + + public: + + Attempt(RESULT result) : _ok(true), _result(result) { } + Attempt(ERROR error) : _ok(false), _error(error) { } + + Attempt(Attempt const &) = default; + Attempt &operator = (Attempt const &) = default; + + template + RET convert(ACCESS_FN const &access_fn, FAIL_FN const fail_fn) const + { + return _ok ? RET { access_fn(_result) } + : RET { fail_fn(_error) }; + } + + template + void with_result(ACCESS_FN const &access_fn, FAIL_FN const fail_fn) const + { + _ok ? access_fn(_result) : fail_fn(_error); + } + + template + void with_error(FAIL_FN const fail_fn) const + { + if (!_ok) + fail_fn(_error); + } + + bool ok() const { return _ok; } + bool failed() const { return !_ok; } +}; + +#endif /* _INCLUDE__UTIL__ATTEMPT_H_ */