Skip to content

Commit f99656d

Browse files
committed
Add TryInto and try_into<T>()
This gives a cast-like syntax for fallably converting to a type in a value-preserving way for chromium#221.
1 parent d81841a commit f99656d

File tree

2 files changed

+44
-13
lines changed

2 files changed

+44
-13
lines changed

subspace/construct/into.h

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
#include "subspace/construct/__private/into_ref.h"
2323
#include "subspace/construct/from.h"
2424
#include "subspace/macros/compiler.h"
25+
#include "subspace/macros/lifetimebound.h"
26+
#include "subspace/mem/forward.h"
2527

2628
namespace sus::construct {
2729

28-
/// A concept that declares `FromType` can be converted to `ToType`, through the
30+
/// A concept that declares `FromType` can be converted to `ToType` through the
2931
/// `From` concept or through an identity transformation.
3032
///
3133
/// When true, `ToType::from(FromType)` can be used to construct `ToType`,
@@ -84,25 +86,22 @@ template <class FromType>
8486
requires(!std::is_const_v<std::remove_reference_t<FromType>> &&
8587
std::is_rvalue_reference_v<FromType &&> &&
8688
!std::is_array_v<FromType>)
87-
constexpr inline auto into(
88-
FromType&& from sus_if_clang([[clang::lifetimebound]])) noexcept {
89+
constexpr inline auto into(FromType&& from sus_lifetimebound) noexcept {
8990
return __private::IntoRef(
9091
static_cast<std::remove_reference_t<FromType>&&>(from));
9192
}
9293

9394
template <class FromType>
9495
requires(std::is_trivially_copyable_v<FromType> && !std::is_array_v<FromType>)
95-
constexpr inline auto into(
96-
const FromType& from sus_if_clang([[clang::lifetimebound]])) noexcept {
96+
constexpr inline auto into(const FromType& from sus_lifetimebound) noexcept {
9797
return __private::IntoRef<const FromType&>(from);
9898
}
9999

100100
template <class FromType>
101101
requires(!std::is_const_v<std::remove_reference_t<FromType>> &&
102102
std::is_trivially_move_constructible_v<FromType> &&
103103
!std::is_array_v<FromType>)
104-
constexpr inline auto into(
105-
FromType& from sus_if_clang([[clang::lifetimebound]])) noexcept {
104+
constexpr inline auto into(FromType& from sus_lifetimebound) noexcept {
106105
return __private::IntoRef(
107106
static_cast<std::remove_reference_t<FromType>&&>(from));
108107
}
@@ -113,7 +112,7 @@ constexpr inline auto into(
113112
/// satisfied.
114113
template <class FromType, size_t N>
115114
constexpr inline auto array_into(
116-
FromType (&from)[N] sus_if_clang([[clang::lifetimebound]])) noexcept {
115+
FromType (&from)[N] sus_lifetimebound) noexcept {
117116
return __private::IntoRefArray<FromType, N>(from);
118117
}
119118

@@ -128,8 +127,7 @@ constexpr inline auto array_into(
128127
template <class FromType>
129128
requires(!std::is_const_v<std::remove_reference_t<FromType>> &&
130129
!std::is_array_v<FromType>)
131-
constexpr inline auto move_into(
132-
FromType& from sus_if_clang([[clang::lifetimebound]])) noexcept {
130+
constexpr inline auto move_into(FromType& from sus_lifetimebound) noexcept {
133131
return __private::IntoRef(
134132
static_cast<std::remove_cv_t<std::remove_reference_t<FromType>>&&>(from));
135133
}
@@ -138,13 +136,37 @@ template <class FromType>
138136
requires(std::is_rvalue_reference_v<FromType &&> &&
139137
!std::is_const_v<std::remove_reference_t<FromType>> &&
140138
!std::is_array_v<FromType>)
141-
constexpr inline auto move_into(
142-
FromType&& from sus_if_clang([[clang::lifetimebound]])) noexcept {
139+
constexpr inline auto move_into(FromType&& from sus_lifetimebound) noexcept {
143140
return __private::IntoRef(
144141
static_cast<std::remove_cv_t<std::remove_reference_t<FromType>>&&>(from));
145142
}
146143

147-
// TODO: Consider adding sus::try_into() and a TryInto concept?
144+
/// A concept that declares `FromType` can (sometimes) be converted to `ToType`
145+
/// through the `TryFrom` concept or through an identity transformation.
146+
template <class FromType, class ToType>
147+
concept TryInto = ::sus::construct::TryFrom<ToType, FromType> ||
148+
std::same_as<ToType, FromType>;
149+
150+
/// Attempts to convert from the given value to a `ToType`.
151+
///
152+
/// Unlike `into()`, this function can not use type deduction to determine the
153+
/// receiving type as it needs to determine the Result type and allow the caller
154+
/// the chance to handle the error condition.
155+
///
156+
/// The `TryFrom` concept requires a `try_from()` method that returns a
157+
/// `Result`. That `Result` will be the return type of this function.
158+
///
159+
/// # Example
160+
/// ```
161+
/// auto valid = sus::try_into<u8>(123_i32).unwrap_or_default();
162+
/// sus::check(valid == 123);
163+
/// auto invalid = sus::try_into<u8>(-1_i32).unwrap_or_default();
164+
/// sus::check(invalid == 0);
165+
/// ```
166+
template <class ToType, TryInto<ToType> FromType>
167+
constexpr inline auto try_into(FromType&& from) noexcept {
168+
return ToType::try_from(::sus::forward<FromType>(from));
169+
}
148170

149171
} // namespace sus::construct
150172

@@ -153,4 +175,5 @@ namespace sus {
153175
using ::sus::construct::array_into;
154176
using ::sus::construct::into;
155177
using ::sus::construct::move_into;
178+
using ::sus::construct::try_into;
156179
} // namespace sus

subspace/construct/into_unittest.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "googletest/include/gtest/gtest.h"
1818
#include "subspace/macros/__private/compiler_bugs.h"
1919
#include "subspace/mem/forward.h"
20+
#include "subspace/prelude.h"
2021
#include "subspace/test/behaviour_types.h"
2122

2223
using sus::construct::From;
@@ -156,4 +157,11 @@ TEST(Into, Concept) {
156157
EXPECT_EQ(f(S(3)).got_value, 3);
157158
}
158159

160+
TEST(TryInto, Example) {
161+
auto valid = sus::try_into<u8>(123_i32).unwrap_or_default();
162+
sus::check(valid == 123u);
163+
auto invalid = sus::try_into<u8>(-1_i32).unwrap_or_default();
164+
sus::check(invalid == 0u);
165+
}
166+
159167
} // namespace

0 commit comments

Comments
 (0)