Microphysics icon indicating copy to clipboard operation
Microphysics copied to clipboard

use std::expected to handle burn failures?

Open BenWibking opened this issue 6 months ago • 4 comments

It would be useful to handle burn/integration failures in a more modern C++ (but exception-free) way.

This could be done by having the burner return an std::expected object (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0323r3.pdf, monadic functions: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2505r1.html)

(Public domain implementation here: https://github.com/TartanLlama/expected.)

It works like this:

enum class arithmetic_errc
{
 divide_by_zero, // 9 / 0 == ?
 not_integer_division, // 5 / 2 == 2.5 (which is not an integer)
 integer_divide_overflows, // INT_MIN / -1
};

expected<double, errc> safe_divide(double i, double j)
{
 if (j == 0) return unexpected(arithmetic_errc::divide_by_zero); // (1)
 else return i / j; // (2)
}

int divide2(int i, int j)
{
 auto e = safe_divide(i, j);
 if (!e)
 switch (e.error().value()) {
 case arithmetic_errc::divide_by_zero: return 0;
 case arithmetic_errc::not_integer_division: return i / j; // Ignore.
 case arithmetic_errc::integer_divide_overflows: return INT_MIN;
 // No default! Adding a new enum value causes a compiler warning here,
 // forcing an update of the code.
 }
 return *e;
}

In principle, std::expected objects can be propagated higher in the call stack, so it could be used thoughout Microphysics, not just in returning to the application code.

BenWibking avatar Jan 03 '24 15:01 BenWibking

What is an example of something this enables that we can't do right now (with the burn status stored in the burn_t)?

maxpkatz avatar Jan 03 '24 15:01 maxpkatz

What is an example of something this enables that we can't do right now (with the burn status stored in the burn_t)?

It can propagate up the call chain into parts of our code that aren't Microphysics-specific. This could reduce the amount of boilerplate code significantly by using the and_then extension (https://github.com/TartanLlama/expected?tab=readme-ov-file#expected).

This extension is now part of C++23: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2505r1.html

For example, we could implement hydro retries like this:

tl::expected<image,fail_reason> attempt_hydro_update (const hydro_state& state) {
    return burn_strang_split(state)
           .and_then(hydro_stage1)
           .and_then(hydro_stage2)
           .and_then(burn_strang_split)
}

BenWibking avatar Jan 03 '24 15:01 BenWibking

it looks like this is a C++23 feature, right?

zingale avatar Jan 03 '24 15:01 zingale

it looks like this is a C++23 feature, right?

Yes. You can also copy this (public domain) header file and use it now with C++17: https://github.com/TartanLlama/expected/blob/v1.1.0/include/tl/expected.hpp.

BenWibking avatar Jan 03 '24 16:01 BenWibking