Skip to content

Commit 97f32a9

Browse files
committed
added narrow cast
added make ready future consistent internal namespace bumped version to 0.4.0
1 parent e343d3f commit 97f32a9

9 files changed

Lines changed: 155 additions & 10 deletions

File tree

include/oryx/crt/argparse.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace oryx::crt {
1111

12-
namespace details {
12+
namespace detail {
1313
template <typename>
1414
constexpr bool always_false = false;
1515
}
@@ -40,7 +40,7 @@ class ArgumentParser {
4040
} else if constexpr (std::is_floating_point_v<T>) {
4141
return FromChars<T>(*vit);
4242
} else {
43-
static_assert(details::always_false<T>,
43+
static_assert(detail::always_false<T>,
4444
"Get argument only supports std::string integral and floating point types");
4545
}
4646
}

include/oryx/crt/cycle_timer.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class CycleTimer {
3232

3333
private:
3434
const Duration target_;
35-
details::StopwatchImpl<Clock> sw_{};
35+
detail::StopwatchImpl<Clock> sw_{};
3636
};
3737

3838
template <class Clock>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
#include <future>
4+
5+
namespace oryx::crt {
6+
7+
template <typename T>
8+
[[nodiscard]] auto MakeReadyFuture(T&& value) {
9+
std::promise<std::decay_t<T>> p;
10+
p.set_value(std::forward<T>(value));
11+
return p.get_future();
12+
}
13+
14+
[[nodiscard]] inline auto MakeReadyFuture() {
15+
std::promise<void> p;
16+
p.set_value();
17+
return p.get_future();
18+
}
19+
20+
} // namespace oryx::crt

include/oryx/crt/narrow_cast.hpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#pragma once
2+
3+
#include <optional>
4+
#include <type_traits>
5+
#include <utility>
6+
7+
namespace oryx::crt {
8+
9+
/**
10+
* @brief Searchable way to do narrowing casts of values
11+
*/
12+
template <class T, class U>
13+
constexpr auto NarrowCast(U&& u) -> T {
14+
return static_cast<T>(std::forward<U>(u));
15+
}
16+
17+
/**
18+
* @brief Checked version of NarrowCast() that returns nullopt if the cast changed the value
19+
*/
20+
template <class T, class U>
21+
constexpr auto Narrow(U u) -> std::optional<T>
22+
requires std::is_arithmetic_v<T>
23+
{
24+
constexpr const bool is_different_signedness = (std::is_signed_v<T> != std::is_signed_v<U>);
25+
26+
const T t = NarrowCast<T>(u);
27+
28+
#if defined(__clang__) || defined(__GNUC__)
29+
#pragma GCC diagnostic push
30+
#pragma GCC diagnostic ignored "-Wfloat-equal"
31+
#endif
32+
// Note: NaN will always return nullopt, since NaN != NaN
33+
if (static_cast<U>(t) != u || (is_different_signedness && ((t < T{}) != (u < U{})))) {
34+
return std::nullopt;
35+
}
36+
#if defined(__clang__) || defined(__GNUC__)
37+
#pragma GCC diagnostic pop
38+
#endif
39+
40+
return t;
41+
}
42+
43+
template <class T, class U>
44+
constexpr auto Narrow(U u) -> std::optional<T>
45+
requires(!std::is_arithmetic_v<T>)
46+
{
47+
const T t = NarrowCast<T>(u);
48+
if (static_cast<U>(t) != u) {
49+
return std::nullopt;
50+
}
51+
52+
return t;
53+
}
54+
55+
} // namespace oryx::crt

include/oryx/crt/stopwatch.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <chrono>
44

55
namespace oryx::crt {
6-
namespace details {
6+
namespace detail {
77

88
template <class Clock>
99
requires std::chrono::is_clock_v<Clock>
@@ -27,9 +27,9 @@ class StopwatchImpl {
2727
Clock::time_point start_;
2828
};
2929

30-
} // namespace details
30+
} // namespace detail
3131

32-
using HighResolutionStopwatch = details::StopwatchImpl<std::chrono::high_resolution_clock>;
33-
using Stopwatch = details::StopwatchImpl<std::chrono::steady_clock>;
32+
using HighResolutionStopwatch = detail::StopwatchImpl<std::chrono::high_resolution_clock>;
33+
using Stopwatch = detail::StopwatchImpl<std::chrono::steady_clock>;
3434

3535
} // namespace oryx::crt

include/oryx/crt/version.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace oryx::crt {
66

77
inline constexpr int kVersionMajor = 0;
8-
inline constexpr int KVersionMinor = 3;
8+
inline constexpr int KVersionMinor = 4;
99
inline constexpr int kVersionPatch = 0;
1010

1111
auto MakeStringVersion() -> std::string;

tests/make_ready_future.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include "doctest.hpp"
2+
3+
#include <oryx/crt/make_ready_future.hpp>
4+
5+
using namespace oryx::crt;
6+
7+
TEST_CASE("future should not throw when calling get()") {
8+
auto fut = MakeReadyFuture(42);
9+
CHECK_NOTHROW(fut.get());
10+
}
11+
12+
TEST_CASE("void future should not throw when calling get()") {
13+
auto fut = MakeReadyFuture();
14+
CHECK_NOTHROW(fut.get());
15+
}
16+
17+
TEST_CASE("get() should return what was passed") {
18+
auto fut = MakeReadyFuture(42);
19+
CHECK(fut.valid());
20+
CHECK(fut.get() == 42);
21+
}

tests/narrow_cast.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include "doctest.hpp"
2+
3+
#include <cstdint>
4+
#include <limits>
5+
6+
#include <oryx/crt/narrow_cast.hpp>
7+
8+
using namespace oryx::crt;
9+
10+
TEST_CASE("same type conversions") {
11+
CHECK(Narrow<int, int>(0) == 0);
12+
CHECK(Narrow<int, int>(42) == 42);
13+
CHECK(Narrow<unsigned, unsigned>(123u) == 123u);
14+
}
15+
16+
TEST_CASE("signed to signed") {
17+
CHECK(Narrow<int8_t, int>(127) == 127);
18+
CHECK(Narrow<int8_t, int>(-128) == -128);
19+
20+
CHECK_FALSE(Narrow<int8_t, int>(128).has_value());
21+
CHECK_FALSE(Narrow<int8_t, int>(-129).has_value());
22+
}
23+
24+
TEST_CASE("unsigned to unsigned") {
25+
CHECK(Narrow<uint8_t, unsigned>(255u) == 255u);
26+
CHECK_FALSE(Narrow<uint8_t, unsigned>(256u).has_value());
27+
}
28+
29+
TEST_CASE("signed to unsigned") {
30+
CHECK(Narrow<unsigned, int>(0) == 0u);
31+
CHECK(Narrow<unsigned, int>(42) == 42u);
32+
33+
CHECK_FALSE(Narrow<unsigned, int>(-1).has_value());
34+
CHECK_FALSE(Narrow<uint8_t, int>(std::numeric_limits<int>::max()).has_value());
35+
}
36+
37+
TEST_CASE("unsigned to signed") {
38+
CHECK(Narrow<int, unsigned>(0u) == 0);
39+
CHECK(Narrow<int, unsigned>(42u) == 42);
40+
41+
CHECK_FALSE(Narrow<int8_t, unsigned>(128u).has_value());
42+
}
43+
44+
TEST_CASE("boundary conditions") {
45+
CHECK(Narrow<int, unsigned>(static_cast<unsigned>(std::numeric_limits<int>::max())) ==
46+
std::numeric_limits<int>::max());
47+
48+
CHECK_FALSE(Narrow<int, unsigned>(static_cast<unsigned>(std::numeric_limits<int>::max()) + 1u).has_value());
49+
}

tests/stopwatch_test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
using namespace oryx::crt;
1010

1111
TEST_CASE("Reset calls clock now") {
12-
details::StopwatchImpl<oryx::crt::ChronoMockClock> sw{};
12+
detail::StopwatchImpl<oryx::crt::ChronoMockClock> sw{};
1313
const auto tp = oryx::crt::ChronoMockClock::time_point{std::chrono::seconds(5)};
1414
oryx::crt::ChronoMockClock::set_time(tp);
1515
sw.Reset();
1616
CHECK_EQ(sw.GetStart(), tp);
1717
}
1818

1919
TEST_CASE("Elapsed and elapsed ms subtracts now from start") {
20-
details::StopwatchImpl<oryx::crt::ChronoMockClock> sw{
20+
detail::StopwatchImpl<oryx::crt::ChronoMockClock> sw{
2121
oryx::crt::ChronoMockClock::time_point{std::chrono::seconds(5)}};
2222
const auto tp = oryx::crt::ChronoMockClock::time_point{std::chrono::seconds(10)};
2323
oryx::crt::ChronoMockClock::set_time(tp);

0 commit comments

Comments
 (0)