Skip to content

add safe_cast #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/safe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
#include <safe/int.hpp>
#include <safe/match.hpp>
#include <safe/object.hpp>
#include <safe/safe_cast.hpp>
#include <safe/value.hpp>
#include <safe/var.hpp>
4 changes: 2 additions & 2 deletions include/safe/detail/fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ template <typename T> struct unsafe_cast_ferry {

template <typename T>
requires(safe::Var<T>)
[[nodiscard]] constexpr auto unsafe_cast(auto const &src) {
[[nodiscard]] SAFE_INLINE constexpr auto unsafe_cast(auto const &src) {
return T{safe::unsafe_cast_ferry{src}};
}

template <typename T>
requires(!safe::Var<T>)
[[nodiscard]] constexpr auto unsafe_cast(auto const &src) {
[[nodiscard]] SAFE_INLINE constexpr auto unsafe_cast(auto const &src) {
return src;
}
17 changes: 17 additions & 0 deletions include/safe/safe_cast.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <safe/detail/concepts.hpp>
#include <safe/detail/fwd.hpp>
#include <safe/var.hpp>

#include <concepts>

template <typename To, safe::Var From>
requires(std::is_convertible_v<typename From::value_type, To>)
[[nodiscard]] SAFE_INLINE constexpr To safe_cast(From const &src) {
static_assert(safe::detail::integral_type<To>::requirement >=
From::requirement,
"The safe value must fit within the target value type.");

return static_cast<To>(src.unsafe_value_);
}
2 changes: 2 additions & 0 deletions test/safe/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_subdirectory(array)
add_subdirectory(var)
add_subdirectory(safe_cast)

function(add_test_suites)
foreach(test_file ${ARGN})
Expand Down Expand Up @@ -34,6 +35,7 @@ add_test_suites(
var.cpp
match.cpp
array.cpp
safe_cast.cpp
dsl/add.cpp
dsl/divide.cpp
dsl/intersection.cpp
Expand Down
33 changes: 33 additions & 0 deletions test/safe/safe_cast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include <cstdint>
#include <type_traits>

#include <safe.hpp>

using ::testing::_;
using ::testing::InSequence;
using ::testing::Return;

using namespace safe::interval_types;
using namespace safe::int_types;
using namespace safe::literals;

TEST(safe_cast_test, cast_same_type) {
auto v = safe_cast<std::int32_t>(42_s32);
EXPECT_EQ(v, 42);
static_assert(std::is_same_v<std::int32_t, decltype(v)>);
}

TEST(safe_cast_test, cast_narrower_type) {
auto v = safe_cast<std::uint8_t>(42_s32);
EXPECT_EQ(v, 42);
static_assert(std::is_same_v<std::uint8_t, decltype(v)>);
}

TEST(safe_cast_test, cast_different_sign) {
auto v = safe_cast<std::uint32_t>(99_s32);
EXPECT_EQ(v, 99);
static_assert(std::is_same_v<std::uint32_t, decltype(v)>);
}
9 changes: 9 additions & 0 deletions test/safe/safe_cast/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function(add_fail_tests)
foreach(name ${ARGN})
add_compile_fail_test("${name}.cpp" LIBRARIES safe_arithmetic)
endforeach()
endfunction()

add_fail_tests(
incompatible_sign_cast
incompatible_range_cast)
9 changes: 9 additions & 0 deletions test/safe/safe_cast/incompatible_range_cast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <cstdint>

#include <safe.hpp>

using namespace safe::interval_types;
using namespace safe::int_types;
using namespace safe::literals;

auto main() -> int { auto v = safe_cast<std::uint8_t>(420_u32); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Add an EXPECT line to ensure a nice error

9 changes: 9 additions & 0 deletions test/safe/safe_cast/incompatible_sign_cast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <cstdint>

#include <safe.hpp>

using namespace safe::interval_types;
using namespace safe::int_types;
using namespace safe::literals;

auto main() -> int { auto v = safe_cast<std::uint32_t>(-99_s32); }