Skip to content

Commit e89ae4c

Browse files
committed
Add u32::from(i32) and i32::from(u32)
Going from one type to the other will panic if out of bounds
1 parent 026dbf0 commit e89ae4c

11 files changed

+140
-7
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ add_library(subspace STATIC
6565
"num/__private/signed_integer_macros.h"
6666
"num/__private/unsigned_integer_macros.h"
6767
"num/i32.h"
68+
"num/integer_concepts.h"
6869
"num/num_concepts.h"
6970
"num/u32.h"
7071
"option/__private/is_option_type.h"

concepts/from.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
namespace sus::concepts::from {
2020

21-
// clang-format off
21+
/// A concept that indicates ToType can be constructed from a FromType, via
22+
/// `ToType::from(FromType)`.
23+
///
2224
template <class ToType, class FromType>
25+
// clang-format off
2326
concept From = requires (FromType&& from) {
2427
{ ToType::from(static_cast<FromType&&>(from)) } -> std::same_as<ToType>;
2528
};

concepts/into.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include <type_traits>
1919

2020
#include "concepts/from.h"
21-
#include "mem/forward.h"
2221

2322
namespace sus::concepts::into {
2423

@@ -43,7 +42,7 @@ struct IntoRef {
4342

4443
} // namespace __private
4544

46-
template<class FromType, class ToType>
45+
template <class FromType, class ToType>
4746
concept Into = ::sus::concepts::from::From<ToType, FromType>;
4847

4948
template <class FromType>

concepts/into_unittest.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "concepts/into.h"
1616

17+
#include "mem/forward.h"
1718
#include "third_party/googletest/googletest/include/gtest/gtest.h"
1819

1920
using sus::concepts::from::From;

num/__private/signed_integer_macros.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
static_assert(true)
4242

4343
#define _sus__signed_impl(T, Bytes, LargerT, UnsignedT, UnsignedSusT) \
44+
_sus__signed_from(T); \
4445
_sus__signed_integer_comparison(T); \
4546
_sus__signed_unary_ops(T, UnsignedT); \
4647
_sus__signed_binary_logic_ops(T); \
@@ -60,6 +61,37 @@
6061
_sus__signed_log(T, UnsignedT); \
6162
_sus__signed_endian(T, UnsignedT, Bytes)
6263

64+
#define _sus__signed_from(T) \
65+
/** Constructs a ##T## from a signed integer type (i8, i16, i32, etc). \
66+
* \
67+
* # Panics \
68+
* The function will panic if the input value is out of range for ##T##. \
69+
*/ \
70+
template <Signed S> \
71+
static constexpr i32 from(S s) noexcept { \
72+
if constexpr (MIN_PRIMITIVE > S::MIN_PRIMITIVE) \
73+
::sus::check(s.primitive_value >= MIN_PRIMITIVE); \
74+
if constexpr (MAX_PRIMITIVE < S::MAX_PRIMITIVE) \
75+
::sus::check(s.primitive_value <= MAX_PRIMITIVE); \
76+
return i32(static_cast<primitive_type>(s.primitive_value)); \
77+
} \
78+
\
79+
/** Constructs a ##T## from an unsigned integer type (u8, u16, u32, etc). \
80+
* \
81+
* # Panics \
82+
* The function will panic if the input value is out of range for ##T##. \
83+
*/ \
84+
template <Unsigned U> \
85+
static constexpr i32 from(U u) noexcept { \
86+
constexpr auto umax = \
87+
static_cast<decltype(U::primitive_value)>(MAX_PRIMITIVE); \
88+
if constexpr (umax < U::MAX_PRIMITIVE) { \
89+
::sus::check(u.primitive_value <= umax); \
90+
} \
91+
return i32(static_cast<primitive_type>(u.primitive_value)); \
92+
} \
93+
static_assert(true)
94+
6395
#define _sus__signed_integer_comparison(T) \
6496
/** Returns true if the current value is positive and false if the number is \
6597
* zero or negative. \

num/__private/unsigned_integer_macros.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
static_assert(true)
3838

3939
#define _sus__unsigned_impl(T, Bytes, LargerT) \
40+
_sus__unsigned_from(T); \
4041
_sus__unsigned_integer_comparison(T); \
4142
_sus__unsigned_unary_ops(T); \
4243
_sus__unsigned_binary_logic_ops(T); \
@@ -56,6 +57,37 @@
5657
_sus__unsigned_log(T); \
5758
_sus__unsigned_endian(T, Bytes)
5859

60+
#define _sus__unsigned_from(T) \
61+
/** Constructs a ##T## from a signed integer type (i8, i16, i32, etc). \
62+
* \
63+
* # Panics \
64+
* The function will panic if the input value is out of range for ##T##. \
65+
*/ \
66+
template <Signed S> \
67+
static constexpr u32 from(S s) noexcept { \
68+
::sus::check(s.primitive_value >= 0); \
69+
constexpr auto umax = static_cast<primitive_type>(S::MAX_PRIMITIVE); \
70+
if constexpr (MAX_PRIMITIVE < umax) \
71+
::sus::check(static_cast<primitive_type>(s.primitive_value) <= \
72+
MAX_PRIMITIVE); \
73+
return u32(static_cast<primitive_type>(s.primitive_value)); \
74+
} \
75+
\
76+
/** Constructs a ##T## from an unsigned integer type (u8, u16, u32, etc). \
77+
* \
78+
* # Panics \
79+
* The function will panic if the input value is out of range for ##T##. \
80+
*/ \
81+
template <Unsigned U> \
82+
static constexpr u32 from(U u) noexcept { \
83+
if constexpr (MIN_PRIMITIVE > U::MIN_PRIMITIVE) \
84+
::sus::check(u.primitive_value >= MIN_PRIMITIVE); \
85+
if constexpr (MAX_PRIMITIVE < U::MAX_PRIMITIVE) \
86+
::sus::check(u.primitive_value <= MAX_PRIMITIVE); \
87+
return u32(static_cast<primitive_type>(u.primitive_value)); \
88+
} \
89+
static_assert(true)
90+
5991
#define _sus__unsigned_integer_comparison(T) \
6092
/** sus::concepts::Eq<##T##> trait. */ \
6193
friend constexpr inline bool operator==(const T& l, const T& r) noexcept { \

num/i32.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "num/__private/literals.h"
2222
#include "num/__private/signed_integer_macros.h"
23+
#include "num/integer_concepts.h"
2324

2425
namespace sus::num {
2526

num/i32_unittest.cc

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,7 +1507,8 @@ TEST(i32, OverflowingPow) {
15071507

15081508
EXPECT_EQ((2_i32).overflowing_pow(5_u32),
15091509
(Tuple<i32, bool>::with(32_i32, false)));
1510-
EXPECT_EQ((2_i32).overflowing_pow(0_u32), (Tuple<i32, bool>::with(1_i32, false)));
1510+
EXPECT_EQ((2_i32).overflowing_pow(0_u32),
1511+
(Tuple<i32, bool>::with(1_i32, false)));
15111512
EXPECT_EQ((i32::MAX()).overflowing_pow(1_u32),
15121513
(Tuple<i32, bool>::with(i32::MAX(), false)));
15131514
EXPECT_EQ((i32::MAX()).overflowing_pow(2_u32),
@@ -1522,8 +1523,7 @@ TEST(i32, CheckedPow) {
15221523
EXPECT_EQ((2_i32).checked_pow(0_u32), Option<i32>::some(1_i32));
15231524
EXPECT_EQ((2_i32).checked_pow(1_u32), Option<i32>::some(2_i32));
15241525
EXPECT_EQ((2_i32).checked_pow(30_u32), Option<i32>::some(1_i32 << 30_u32));
1525-
EXPECT_EQ((1_i32).checked_pow(u32::MAX()),
1526-
Option<i32>::some(1_i32));
1526+
EXPECT_EQ((1_i32).checked_pow(u32::MAX()), Option<i32>::some(1_i32));
15271527
EXPECT_EQ((i32::MAX()).checked_pow(1_u32), Option<i32>::some(i32::MAX()));
15281528
EXPECT_EQ((i32::MAX()).checked_pow(0_u32), Option<i32>::some(1_i32));
15291529

@@ -1899,4 +1899,17 @@ TEST(i32, ToNeBytes) {
18991899
}
19001900
}
19011901

1902+
TEST(i32, From) {
1903+
static_assert(sus::concepts::from::From<i32, u32>);
1904+
1905+
// TODO: Add all the integer types as they exist.
1906+
EXPECT_EQ(i32::from(2_i32), 2_i32);
1907+
EXPECT_EQ(i32::from(2_u32), 2_i32);
1908+
}
1909+
1910+
TEST(i32DeathTest, FromOutOfRange) {
1911+
// TODO: Add all the integer types as they exist.
1912+
EXPECT_DEATH(i32::from(u32::MAX()), "");
1913+
}
1914+
19021915
} // namespace

num/integer_concepts.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <concepts>
18+
19+
namespace sus::num {
20+
21+
struct i32;
22+
struct u32;
23+
24+
// TODO: Add all the unsigned types as they exist.
25+
template <class T>
26+
concept Unsigned = std::same_as<u32, std::decay_t<T>>;
27+
28+
// TODO: Add all the signed types as they exist.
29+
template <class T>
30+
concept Signed = std::same_as<i32, std::decay_t<T>>;
31+
32+
template <class T>
33+
concept Integer = Unsigned<T> || Signed<T>;
34+
35+
} // namespace sus::num

num/u32.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
#include <concepts>
2020

21+
#include "concepts/from.h"
22+
#include "concepts/into.h"
2123
#include "num/__private/literals.h"
2224
#include "num/__private/unsigned_integer_macros.h"
25+
#include "num/integer_concepts.h"
2326

2427
namespace sus::num {
2528

@@ -74,7 +77,6 @@ struct u32 {
7477
// TODO: from_str_radix(). Need Result type and Errors.
7578
// TODO: next_power_of_two().
7679
// TODO: checked_add_signed() and friends?
77-
// TODO: casting to i32, and u{8-64}. Maybe i{8-64}?
7880

7981
/// The inner primitive value, in case it needs to be unwrapped from the
8082
/// type. Avoid using this member except to convert when a consumer

num/u32_unittest.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "concepts/into.h"
2020
#include "concepts/make_default.h"
21+
#include "num/i32.h"
2122
#include "mem/__private/relocate.h"
2223
#include "num/num_concepts.h"
2324
#include "option/option.h"
@@ -1218,4 +1219,17 @@ TEST(u32, ToNeBytes) {
12181219
}
12191220
}
12201221

1222+
TEST(u32, From) {
1223+
static_assert(sus::concepts::from::From<u32, i32>);
1224+
1225+
// TODO: Add all the integer types as they exist.
1226+
EXPECT_EQ(u32::from(2_i32), 2_u32);
1227+
EXPECT_EQ(u32::from(2_u32), 2_u32);
1228+
}
1229+
1230+
TEST(u32DeathTest, FromOutOfRange) {
1231+
// TODO: Add all the integer types as they exist.
1232+
EXPECT_DEATH(u32::from(-1_i32), "");
1233+
}
1234+
12211235
} // namespace

0 commit comments

Comments
 (0)