3232# include < variant>
3333#endif
3434
35+ #ifdef __cpp_lib_expected
36+ # include < expected>
37+ #endif
38+
3539PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE)
3640PYBIND11_NAMESPACE_BEGIN(detail)
3741
@@ -424,6 +428,47 @@ struct variant_caster<V<Ts...>> {
424428 + const_name(" ]" ));
425429};
426430
431+ // / Generic expected caster
432+ template <typename E>
433+ struct expected_caster ;
434+
435+ template <template <typename , typename > class E , typename TValue, typename TError>
436+ struct expected_caster <E<TValue, TError>> {
437+ using expected_type = E<TValue, TError>;
438+
439+ // I'm not sure what we should call this, as we never actually hold it
440+ // on the python side, it's exclusively deconstructed. In our case, we really
441+ // only care about the conversion from C++ to Python.
442+ // Maybe something like `Union[TValue, TError]`?
443+ PYBIND11_TYPE_CASTER (expected_type, pybind11::detail::const_name(" expected" ));
444+
445+ // load() is not implemented - we are only interested in unwrapping expected
446+ // types from C++, not re-wrapping them.
447+
448+ // This is templated so that it takes a universal reference rather than an
449+ // rvalue reference.
450+ static pybind11::handle cast (std::same_as<expected_type> auto &&src,
451+ pybind11::return_value_policy policy,
452+ pybind11::handle parent) {
453+ if (src.has_value ()) {
454+ return pybind11::detail::make_caster<TValue>::cast (
455+ std::forward<expected_type>(src).value (), policy, parent);
456+ } else {
457+ // The goal here is to defer to __repr__ for consistent formatting with
458+ // Python
459+ if constexpr (std::same_as<TError, std::string>) {
460+ // Optimize in this case, no need to convert to python to get its
461+ // representation
462+ throw std::runtime_error (std::forward<expected_type>(src).error ());
463+ } else {
464+ auto str = pybind11::detail::make_caster<TError>::cast (
465+ std::forward<expected_type>(src).error (), policy, parent);
466+ throw std::runtime_error (pybind11::repr (std::move (str)));
467+ }
468+ }
469+ }
470+ };
471+
427472#if defined(PYBIND11_HAS_VARIANT)
428473template <typename ... Ts>
429474struct type_caster <std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {};
@@ -432,6 +477,12 @@ template <>
432477struct type_caster <std::monostate> : public void_caster<std::monostate> {};
433478#endif
434479
480+ #ifdef __cpp_lib_expected
481+ template <typename TValue, typename TError>
482+ struct pybind11 ::detail::type_caster<std::expected<TValue, TError>>
483+ : expected_caster<std::expected<TValue, TError>> {};
484+ #endif
485+
435486PYBIND11_NAMESPACE_END (detail)
436487
437488inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
0 commit comments