Skip to content
Lukas Dürrenberger edited this page Jun 22, 2022 · 2 revisions

Links

Current Situation

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().

Target Situation

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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).
  5. Result type Inspired by std::expected proposal or Rust's Result type. Basically std::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 an ignore() 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.
Clone this wiki locally