-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Back in 2015, I created several forum threads around the topic of error handling/reporting:
Some discussions around the topic date back to 2011 and 2013:
These are quite interesting, already to understand historic context and how SFML 1 and 2 evolved to be without exceptions.
Error handling in SFML 3
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 userfriendlyness. 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 2sf::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 ofboolexcept 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
noexceptrabbit hole. - ❌ Need verbose
try/catchblock 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 bystd::expectedproposal or Rust'sResulttype.
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 (