Skip to content

Commit

Permalink
Add docs and examples for conversions
Browse files Browse the repository at this point in the history
Move the cast conversion comparison table up to the construct namespace
docs. Add examples to the into function.
  • Loading branch information
danakj committed Dec 25, 2023
1 parent ca224fc commit ede88ae
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 30 deletions.
30 changes: 10 additions & 20 deletions sus/construct/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,39 +127,29 @@ concept Cast = requires(const From& from) {
};

/// An infallible conversion (cast) that may lose the original
/// value in the process. If the input can not be represented in the output,
/// value in the process.
///
/// See the [namespace level documentation]($sus::option) for more about
/// converting types.
///
/// If the input can not be represented in the output,
/// some other value will be produced, which may lead to application bugs and
/// memory unsafety if used incorrectly. This behaves like `static_cast<To>()`
/// but without Undefined Behaviour.
/// but does not cause Undefined Behaviour for any input
/// and output type. without Undefined Behaviour.
///
/// The [`cast`]($sus::construct::cast) operation is supported for types
/// `To` and `From` that satisfy [`Cast<To, From>`](
/// $sus::construct::Cast).
///
/// Usually prefer to convert between types with the value-preserving methods
/// of [`From`]($sus::construct::From) and
/// [`Into`]($sus::construct::Into) and [`TryInto`]($sus::construct::TryInto)
/// when possible. [`Cast`]($sus::construct::Cast) is required for converting
/// from floating point to integer values, and from larger integer types to
/// floating point, as these are lossy conversions.
///
/// | Concept | Usage | Infallible | Preserves values |
/// | ------- | ----- | ---------- | ---------------- |
/// | [`From`]($sus::construct::From) / [`Into`]($sus::construct::Into) | `T::from(x)` / [`sus::into(x)`]($sus::construct::into) | ✅ | ✅ |
/// | [`TryFrom`]($sus::construct::TryFrom) / [`TryInto`]($sus::construct::TryInto) | `T::try_from(x)` / [`sus::try_into<T>(x)`]($sus::construct::try_into) | ❌ | ✅ |
/// | [`Cast`]($sus::construct::Cast) | `sus::cast<T>(x)` | ✅ | ❌ |
///
/// See [`Cast`]($sus::construct::Cast) for how numeric and
/// primitive values are converted.
///
/// It is best practice to place a `// SAFETY:` comment on use of [`sus::cast`](
/// $sus::construct::cast) in order to explain why the code intends to change
/// the value during the cast.
///
/// # Examples
///
/// This converts `-1_i64` into a `u32`, which both changes its meaning,
/// becoming a large positive number, and truncates the high 32 bits, losing the
/// This converts `-1` as an `i64` into a `u32`, which both changes its meaning,
/// becoming a large positive number, and truncates the high 32 bits losing the
/// original bits.
/// ```cpp
/// // SAFETY: We're intending to convert negative numbers into large positive
Expand Down
43 changes: 34 additions & 9 deletions sus/construct/construct.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,45 @@ namespace sus {

/// Concepts and functions for constructing and converting between types.
///
/// The [`cast`]($sus::construct::cast) function is built on the
/// [`Cast`]($sus::construct::Cast) concept to convert between numeric and
/// primitive types in a safer way than `static_cast`.
/// # Type conversions
///
/// The [`From`]($sus::construct::From) and [`Into`]($sus::construct::Into)
/// concepts allow converting in a lossless and infallible way between types,
/// and accepting generics that can convert to a desired type. The
/// [`into`]($sus::construct::into) function allows explicit conversion while
/// deducing the target type, such as for converting function arguments or
/// return values.
/// This namespace provides tools for three general methods of converting
/// between types:
/// * Infallible conversions which preserve values:
/// [`From`]($sus::construct::From) / [`Into`]($sus::construct::Into)
/// * Fallible conversions which preserve values or fail explicitly:
/// [`TryFrom`]($sus::construct::TryFrom) /
/// [`TryInto`]($sus::construct::TryInto)
/// * Infallib;e conversions which can change values or lose data:
/// [`Cast`]($sus::construct::Cast)
///
/// Usually prefer to convert between types with the value-preserving methods
/// of [`From`]($sus::construct::From) and
/// [`Into`]($sus::construct::Into) and [`TryInto`]($sus::construct::TryInto)
/// when possible. [`Cast`]($sus::construct::Cast) is required for converting
/// from floating point to integer values, and from larger integer types to
/// floating point, as these are lossy conversions.
///
/// The [`into`]($sus::construct::into) function allows explicit conversion
/// while deducing the target type, such as for converting function arguments
/// or return values.
///
/// | Concept | Usage | Infallible | Preserves values |
/// | ------- | ----- | ---------- | ---------------- |
/// | [`From`]($sus::construct::From) / [`Into`]($sus::construct::Into) | `T::from(x)` / [`sus::into(x)`]($sus::construct::into) | ✅ | ✅ |
/// | [`TryFrom`]($sus::construct::TryFrom) / [`TryInto`]($sus::construct::TryInto) | `T::try_from(x)` / [`sus::try_into<T>(x)`]($sus::construct::try_into) | ❌ | ✅ |
/// | [`Cast`]($sus::construct::Cast) | [`sus::cast<T>(x)`]($sus::construct::cast) | ✅ | ❌ |
///
/// See [`Cast`]($sus::construct::Cast) for how numeric and
/// primitive values are converted with [`cast`]($sus::construct::cast).
///
/// # Default construction
///
/// The [`Default`]($sus::construct::Default) concept matches types which can be
/// default constructed, allowing their use in generic code.
///
/// # Constructing from and holding references
///
/// The [`SafelyConstructibleFromReference`](
/// $sus::construct::SafelyConstructibleFromReference) concept pairs with the
/// `[[clang::lifetimebound]]` attribute, which gives better protection with
Expand Down
25 changes: 24 additions & 1 deletion sus/construct/into.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,29 @@ concept Into =
/// If the argument to `into` is [`Copy`]($sus::mem::Copy) then it will be
/// copied if it is an lvalue or const. If the argument to `into` is an rvalue,
/// it will be moved when constructing the `ToType`.
///
/// # Examples
/// The `into` function deduces the target type while performing an *explicit*
/// conversion via the [`From`]($sus::construct::From) concept (and thus a
/// static `from` method on the target type).
/// ```
/// auto f = [](Option<i32> i) { return i.unwrap_or(-1); };
/// auto num = 3_i32;
/// // Option<T> can be converted into from its inner type T.
/// sus_check(f(sus::into(num)) == 3);
/// ```
///
/// The [`Into`]($sus::construct::Into) concept allows generic code to accept
/// any input type that can be explicitly converted into the target type. The
/// body of the function can use the [`into`]($sus::construct::into) function to
/// perform the conversion.
/// ```
/// // f() accepts anything that can be converted to Option<i32> via into().
/// auto f = [](Into<Option<i32>> auto in) { return Option<i32>(sus::into(in)); };
/// auto num = 3_i32;
/// // num will be passed to Option<i32>::from() inside f().
/// sus_check(f(num).unwrap_or(-1) == 3);
/// ```
template <class FromType>
constexpr inline auto into(FromType&& from sus_lifetimebound) noexcept {
return __private::IntoRef<FromType&&>(::sus::forward<FromType>(from));
Expand Down Expand Up @@ -156,7 +179,7 @@ concept TryInto = ::sus::construct::TryFrom<ToType, FromType> ||
/// [`Result`]($sus::result::Result). That [`Result`]($sus::result::Result)
/// will be the return type of this function.
///
/// # Example
/// # Examples
/// ```
/// auto valid = sus::try_into<u8>(123_i32).unwrap_or_default();
/// sus_check(valid == 123);
Expand Down
15 changes: 15 additions & 0 deletions sus/construct/into_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,19 @@ TEST(Into, Ref) {
EXPECT_EQ(&c.as_value(), &s);
}

TEST(Into, Into_Example) {
auto f = [](Option<i32> i) { return i.unwrap_or(-1); };
auto num = 3_i32;
// Option<T> can be converted into from its inner type T.
sus_check(f(sus::into(num)) == 3);
}

TEST(Into, IntoConcept_Example) {
// f() accepts anything that can be converted to Option<i32> via into().
auto f = [](Into<Option<i32>> auto in) { return Option<i32>(sus::into(in)); };
auto num = 3_i32;
// num will be passed to Option<i32>::from() inside f().
sus_check(f(num).unwrap_or(-1) == 3);
}

} // namespace

0 comments on commit ede88ae

Please sign in to comment.