-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
SD: Error Handling
- Ongoing feature discussion
- 2015
- 2011-2013
These are quite interesting, already to understand historic context and how SFML 1 and 2 evolved to be without exceptions.
SFML 2 is currently using boolean return types and logs additional error information to sf::err()
.
Now the year is 2022, a lot has changed, and at last we have the option to introduce breaking changes and redesign the API for better user friendly ness. Handling errors is an important topic, and there are different options to tackle them in SFML 3. I'll jump right into them:
-
Boolean return types E.g. SFML 2
sf::Image::loadFromFile()
- ✔️ Simple.
- ✔️ Easy to port to bindings.
- ❌ Can be forgotten (
[[nodiscard]]
is needed on every single method). - ❌ Needs separate output parameters.
- ❌ No way to transport error messages, needs
sf::err()
side channel.
-
Optionals Basically all the disadvantages of
bool
except no need for output parameters.- ✔️ Simple to understand and document.
- ✔️ Easy to port to bindings.
- ❌ Can be forgotten (
[[nodiscard]]
is needed on every single method). - ❌ No way to transport error messages.
-
Assertions Listed for completeness, but they are not really a solution for runtime errors. But we might want to use them more often to complement other error mechanisms in the case of logic errors.
- ✔️ Immediate breakpoint, must be fixed.
- ✔️ No runtime overhead in release.
- ❌ Only useful for errors that are bugs (not loading files etc).
- ❌ Do not allow the user to be lenient about certain errors.
-
Exceptions
- ✔️ User cannot ignore them.
- ✔️ Automatically propagate upward.
- ✔️ Can be polymorphic, allowing automatic fallbacks and higher-level handling.
- ✔️ Can theoretically transport extra data beyond message, although rarely done in practice.
- ✔️ Can be used in constructors.
- ❌ Hard to document and follow, especially once multiple layers/callbacks/virtual functions are involved.
- ❌ Exception safety can be hard to achieve, and we need to enter the whole
noexcept
rabbit hole. - ❌ Need verbose
try/catch
block even in situations where the user explicitly wants to ignore them. - ❌ Not easy to map to bindings, especially due to polymorphism.
- ❌ Small runtime overhead (more or less depending on exception mechanism).
-
Result type Inspired by
std::expected
proposal or Rust'sResult
type. Basicallystd::variant<T, E>
but with a highly specialized API.- ✔️ User cannot forget them (
[[nodiscard]]
can be declared directly on the type). - ✔️ User can however explicitly and concisely ignore them (e.g. with
(void)
or anignore()
method). - ✔️ Allow to transport arbitrary data as part of the error (e.g. an enum, message, context info).
- ✔️ Function signature makes immediately obvious which methods can fail, and how they can do so -- users are not surprised by unexpected errors and cannot miss any documentation.
- ❌ No automatic propagation (could probably be made somewhat ergonomic as a library solution).
- ❌ Require static factory functions such as
Image::fromFile()
, no constructor support (if this is truly a disadvantage). - ❌ Non-polymorphic unless explicitly designed.
- ❌ Requires either custom-built type for SFML or use of an established library.
- ✔️ User cannot forget them (