diff --git a/docs/design_rationale.md b/docs/design_rationale.md index bdb7918..4a27e68 100644 --- a/docs/design_rationale.md +++ b/docs/design_rationale.md @@ -246,7 +246,7 @@ class value_or_t { using Traits = bms::get_box_traits; if (Traits::has_value(box)) return Traits::value(std::forward(box)); - return std::forward(op).callable(key); + return std::forward(op).identity(key); } }; @@ -263,15 +263,17 @@ static_assert(v == 42); `pipe_adaptor` is the callable object users invoke (e.g. `value_or(42)`). Calling it produces a `closure` that derives from `value_or_t` and stores the argument. The `friend operator|` is found via the derived type and retrieves the stored argument via -`op.callable(key)`, where `key` is an `access_key` that only `value_or_t` can -construct — preventing external code from bypassing the access protocol. +`op.identity(key)` — named after the identity function `id(x) = x`, because the accessor +simply returns whatever was stored unchanged, whether that is a callable or a plain value. +`key` is an `access_key` that only `value_or_t` can construct, preventing +external code from bypassing the access protocol. -> **Note — why `access_key` instead of `op.callable({})`?** +> **Note — why `access_key` instead of `op.identity({})`?** > On GCC and Clang the key can be replaced by a default-constructed sentinel: -> `return std::forward(op).callable({});`. -> This works because `closure::callable` is only accessible from within `value_or_t` anyway +> `return std::forward(op).identity({});`. +> This works because `closure::identity` is only accessible from within `value_or_t` anyway > (the `friend operator|` is a member of `value_or_t`). However, MSVC incorrectly accepts -> `op.callable({})` from outside the class in some contexts, breaking the access restriction. +> `op.identity({})` from outside the class in some contexts, breaking the access restriction. > `access_key` is used to work around this MSVC bug: its constructor is private > and only `value_or_t` can name the type, so the access protocol is enforced on all three > compilers. diff --git a/include/beman/monadics/detail/and_then.hpp b/include/beman/monadics/detail/and_then.hpp index ec12814..fff0b98 100644 --- a/include/beman/monadics/detail/and_then.hpp +++ b/include/beman/monadics/detail/and_then.hpp @@ -27,13 +27,13 @@ class and_then_t { template Op> [[nodiscard]] friend constexpr decltype(auto) operator|(Box&& box, Op&& op) - requires and_thenable_impl(op).callable(key))> + requires and_thenable_impl(op).identity(key))> { using Traits = get_box_traits; - using NewBox = decltype(invoke_with_value(std::forward(op).callable(key), std::forward(box))); + using NewBox = decltype(invoke_with_value(std::forward(op).identity(key), std::forward(box))); if (Traits::has_value(box)) { - return invoke_with_value(std::forward(op).callable(key), std::forward(box)); + return invoke_with_value(std::forward(op).identity(key), std::forward(box)); } return propagate_error(std::forward(box)); diff --git a/include/beman/monadics/detail/or_else.hpp b/include/beman/monadics/detail/or_else.hpp index 156aac5..cf3c933 100644 --- a/include/beman/monadics/detail/or_else.hpp +++ b/include/beman/monadics/detail/or_else.hpp @@ -28,13 +28,13 @@ class or_else_t { template Op> [[nodiscard]] friend constexpr decltype(auto) operator|(Box&& box, Op&& op) - requires or_elseable_impl(op).callable(key))> + requires or_elseable_impl(op).identity(key))> { using Traits = get_box_traits; - using NewBox = decltype(invoke_with_error(std::forward(op).callable(key), std::forward(box))); + using NewBox = decltype(invoke_with_error(std::forward(op).identity(key), std::forward(box))); if (!Traits::has_value(box)) { - return invoke_with_error(std::forward(op).callable(key), std::forward(box)); + return invoke_with_error(std::forward(op).identity(key), std::forward(box)); } return propagate_value(std::forward(box)); diff --git a/include/beman/monadics/detail/pipe_adaptor.hpp b/include/beman/monadics/detail/pipe_adaptor.hpp index 0703945..0563dd3 100644 --- a/include/beman/monadics/detail/pipe_adaptor.hpp +++ b/include/beman/monadics/detail/pipe_adaptor.hpp @@ -22,15 +22,15 @@ struct pipe_adaptor { // msvc bug does not allow to use Callable in noexcept constexpr explicit closure(Fn&& f) noexcept(std::is_nothrow_constructible_v, Fn>) - : callable_(std::forward(f)) {} + : identity_(std::forward(f)) {} - constexpr Callable& callable(access_key) & noexcept { return callable_; } - constexpr const Callable& callable(access_key) const& noexcept { return callable_; } - constexpr Callable&& callable(access_key) && noexcept { return std::move(callable_); } - constexpr const Callable&& callable(access_key) const&& noexcept { return std::move(callable_); } + constexpr Callable& identity(access_key) & noexcept { return identity_; } + constexpr const Callable& identity(access_key) const& noexcept { return identity_; } + constexpr Callable&& identity(access_key) && noexcept { return std::move(identity_); } + constexpr const Callable&& identity(access_key) const&& noexcept { return std::move(identity_); } private: - Callable callable_; + Callable identity_; }; return closure{std::forward(fn)}; diff --git a/include/beman/monadics/detail/transform.hpp b/include/beman/monadics/detail/transform.hpp index cb932e5..1b8e941 100644 --- a/include/beman/monadics/detail/transform.hpp +++ b/include/beman/monadics/detail/transform.hpp @@ -26,18 +26,18 @@ class transform_t { template Op, typename Traits = get_box_traits> [[nodiscard]] friend constexpr decltype(auto) operator|(Box&& box, Op&& op) - requires transform_impl(op).callable(key))> + requires transform_impl(op).identity(key))> { - using NewValue = decltype(invoke_with_value(std::forward(op).callable(key), std::forward(box))); + using NewValue = decltype(invoke_with_value(std::forward(op).identity(key), std::forward(box))); using NewBox = typename Traits::template rebind; using NewBoxTraits = get_box_traits; if (Traits::has_value(box)) { if constexpr (std::is_void_v) { - invoke_with_value(std::forward(op).callable(key), std::forward(box)); + invoke_with_value(std::forward(op).identity(key), std::forward(box)); return NewBoxTraits::make(); } else { - return NewBoxTraits::make(invoke_with_value(std::forward(op).callable(key), + return NewBoxTraits::make(invoke_with_value(std::forward(op).identity(key), std::forward(box))); } } diff --git a/include/beman/monadics/detail/transform_error.hpp b/include/beman/monadics/detail/transform_error.hpp index f1ad663..ca1b5aa 100644 --- a/include/beman/monadics/detail/transform_error.hpp +++ b/include/beman/monadics/detail/transform_error.hpp @@ -24,22 +24,22 @@ class transform_error_t { template Op> [[nodiscard]] friend constexpr decltype(auto) operator|(Box&& box, Op&& op) - requires transform_errorable_impl(op).callable(key))> + requires transform_errorable_impl(op).identity(key))> { using Traits = get_box_traits; - using NewError = decltype(invoke_with_error(std::forward(op).callable(key), std::forward(box))); + using NewError = decltype(invoke_with_error(std::forward(op).identity(key), std::forward(box))); using NewBox = typename Traits::template rebind_error; using NewBoxTraits = get_box_traits; if (!Traits::has_value(box)) { - return NewBoxTraits::make_error(invoke_with_error(std::forward(op).callable(key), + return NewBoxTraits::make_error(invoke_with_error(std::forward(op).identity(key), std::forward(box))); } return propagate_value(std::forward(box)); // gcc11/12 internal crash with pipe_adaptor_t - // return std::forward(box) | or_else([f = std::forward(op).callable(key)](auto&& e) { + // return std::forward(box) | or_else([f = std::forward(op).identity(key)](auto&& e) { // return NewBoxTraits::make_error(f(std::forward(e))); // }); } diff --git a/tests/beman/monadics/detail/pipe_adaptor.test.cpp b/tests/beman/monadics/detail/pipe_adaptor.test.cpp index d88f8f9..ac2da1e 100644 --- a/tests/beman/monadics/detail/pipe_adaptor.test.cpp +++ b/tests/beman/monadics/detail/pipe_adaptor.test.cpp @@ -13,7 +13,7 @@ struct test_op_t { template Op> [[nodiscard]] friend constexpr auto operator|(Box&& box, Op&& op) { - return std::forward(op).callable(key)(std::forward(box)); + return std::forward(op).identity(key)(std::forward(box)); } }; @@ -37,8 +37,8 @@ TEST_CASE("lvalue-callable-is-copied") { return test_op(fn); }(); - STATIC_REQUIRE(closure.callable(test_op_t::key).tracker.copies == 1); - STATIC_REQUIRE(closure.callable(test_op_t::key).tracker.moves == 0); + STATIC_REQUIRE(closure.identity(test_op_t::key).tracker.copies == 1); + STATIC_REQUIRE(closure.identity(test_op_t::key).tracker.moves == 0); } TEST_CASE("vvalue-callable-is-copied") { @@ -53,8 +53,8 @@ TEST_CASE("vvalue-callable-is-copied") { return test_op(std::move(fn)); }(); - STATIC_REQUIRE(closure.callable(test_op_t::key).tracker.copies == 0); - STATIC_REQUIRE(closure.callable(test_op_t::key).tracker.moves == 1); + STATIC_REQUIRE(closure.identity(test_op_t::key).tracker.copies == 0); + STATIC_REQUIRE(closure.identity(test_op_t::key).tracker.moves == 1); } TEST_CASE("fn-stored-by-value-no-dangling") {