Skip to content

Commit 2c830eb

Browse files
authored
Extend generator support to non-copyable types (#474)
* Extend generator support to non-copyable types * Handle assosiative container ranges * Fallback for GCC14 only
1 parent 4556f22 commit 2c830eb

6 files changed

Lines changed: 62 additions & 13 deletions

File tree

libraries/core/lib/morpheus/core/concurrency/generator.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ struct Generator
6565
template <std::convertible_to<T> From>
6666
auto yield_value(From&& from)
6767
{
68-
current_value = std::forward<From>(from);
68+
current_value = std::addressof(from);
6969
return coro::suspend_always{};
7070
}
7171

7272
void unhandled_exception() { exception = std::current_exception(); }
7373

74-
T current_value;
74+
std::add_pointer_t<std::add_const_t<T>> current_value = nullptr;
7575
std::exception_ptr exception;
7676
};
7777

@@ -116,7 +116,7 @@ struct Generator
116116
[[nodiscard]] reference& operator*() const noexcept
117117
{
118118
assert(!handle.done() && "Can't dereference generator end iterator");
119-
return handle.promise().current_value;
119+
return *handle.promise().current_value;
120120
}
121121

122122
[[nodiscard]] pointer& operator->() const noexcept { return std::addressof(operator*()); }

libraries/core/lib/morpheus/core/serialisation/adapters/std/pair.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ T deserialise(Serialiser& serialiser, std::type_identity<T>)
3333
static_assert(!std::is_reference_v<typename T::second_type>);
3434

3535
auto const scope = makeScopedSequence(serialiser.reader(), std::tuple_size<T>::value);
36-
return T{serialiser.template deserialise<typename T::first_type>(), serialiser.template deserialise<typename T::second_type>()};
36+
return T{serialiser.template deserialise<std::remove_const_t<typename T::first_type>>(),
37+
serialiser.template deserialise<std::remove_const_t<typename T::second_type>>()};
3738
}
3839

3940
} // namespace morpheus::serialisation::detail

libraries/core/lib/morpheus/core/serialisation/adapters/std/ranges.hpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
// IWYU pragma: always_keep
4+
#include "morpheus/core/base/compiler.hpp"
45
#include "morpheus/core/conformance/ranges.hpp"
56
#include "morpheus/core/meta/concepts/string.hpp"
67
#include "morpheus/core/serialisation/concepts/read_serialisable.hpp"
@@ -35,6 +36,24 @@ void serialise(Serialiser& serialiser, IsRange auto const& range)
3536
serialiser.writer().endSequence();
3637
}
3738

39+
template <typename T>
40+
concept IsAssociativeLike = requires {
41+
typename T::value_type;
42+
typename T::key_type;
43+
};
44+
45+
// template <typename T>
46+
// struct RangeDeserialiseValue
47+
//{
48+
// using type = conf::ranges::range_value_t<T>;
49+
// };
50+
//
51+
// template <IsMapLike T>
52+
// struct RangeDeserialiseValue<T>
53+
//{
54+
// using type = std::pair<typename T::key_type, typename T::mapped_type>;
55+
// };
56+
3857
template <concepts::ReadSerialiser Serialiser, IsRange T>
3958
void deserialise(Serialiser& serialiser, T& range)
4059
{
@@ -69,14 +88,25 @@ void deserialise(Serialiser& serialiser, T& range)
6988
template <concepts::ReadSerialiser Serialiser, IsRange T>
7089
T deserialise(Serialiser& serialiser, std::type_identity<T>)
7190
{
72-
#if defined(__cpp_lib_ranges_to_container) && __cpp_lib_ranges_to_container >= 202202L
7391
using ValueType = conf::ranges::range_value_t<T>;
74-
auto seq = serialiser.template sequence<ValueType>();
75-
return conf::ranges::to<T>(std::move(seq));
92+
93+
#if defined(__cpp_lib_ranges_to_container) && __cpp_lib_ranges_to_container >= 202202L
94+
static constexpr bool isGcc14 = (MORPHEUS_COMPILER == MORPHEUS_GNUC_COMPILER) && (MORPHEUS_COMP_VER >= 140000000) && (MORPHEUS_COMP_VER < 150000000);
95+
96+
if constexpr (isGcc14 && IsAssociativeLike<T>)
97+
{
98+
// We must work around Gcc's std::ranges::to implementation for associative containers:
99+
// https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#4121
100+
T range;
101+
serialiser.deserialise(range);
102+
return range;
103+
}
104+
else
105+
{
106+
return conf::ranges::to<T>(serialiser.template sequence<ValueType>());
107+
}
76108
#elif defined(__cpp_lib_containers_ranges) && __cpp_lib_containers_ranges >= 202202L
77-
using ValueType = conf::ranges::range_value_t<T>;
78-
auto seq = serialiser.template sequence<ValueType>();
79-
return T(std::from_range, std::move(seq));
109+
return T(std::from_range, serialiser.template sequence<ValueType>());
80110
#else
81111
T range;
82112
serialiser.deserialise(range);

libraries/core/lib/morpheus/core/serialisation/adapters/std/tuple.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ T deserialise(Serialiser& serialiser, std::type_identity<T>)
3333
{
3434
// More work required to support std::tuples containing references.
3535
static_assert((!std::is_reference_v<std::tuple_element_t<Indexes, T>> || ...));
36-
return T{serialiser.template deserialise<std::tuple_element_t<Indexes, T>>()...};
36+
return T{serialiser.template deserialise<std::remove_const_t<std::tuple_element_t<Indexes, T>>>()...};
3737
}(std::make_index_sequence<size>());
3838
}
3939

libraries/core/tests/serialisation/json_reader.tests.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "morpheus/core/serialisation/adapters/std/bitset.hpp"
77
#include "morpheus/core/serialisation/adapters/std/chrono.hpp"
88
#include "morpheus/core/serialisation/adapters/std/indirect.hpp"
9+
#include "morpheus/core/serialisation/adapters/std/map.hpp"
910
#include "morpheus/core/serialisation/adapters/std/monostate.hpp"
1011
#include "morpheus/core/serialisation/adapters/std/optional.hpp"
1112
#include "morpheus/core/serialisation/adapters/std/pair.hpp"
@@ -575,6 +576,15 @@ TEST_CASE("Json reader can read std types from underlying text representation",
575576
REQUIRE(test::deserialise<std::chrono::years>(R"("100y")") == std::chrono::years{100});
576577
REQUIRE(test::deserialise<std::chrono::months>(R"("12m")") == std::chrono::months{12});
577578
}
579+
SECTION("Container types")
580+
{
581+
REQUIRE(test::deserialise<std::map<int, std::string>>(R"([[1,"a"],[2,"b"],[3,"b"]])") == std::map<int, std::string>{
582+
{1, "a"},
583+
{2, "b"},
584+
{3, "b"}
585+
});
586+
REQUIRE(test::deserialise<std::vector<int>>(R"([1, 2, 3, 4, 5])") == std::vector<int>{1, 2, 3, 4, 5});
587+
}
578588
REQUIRE(test::deserialise<std::bitset<4>>(R"("1101")") == std::bitset<4>("1101"));
579589
REQUIRE(test::deserialise<conf::vt::indirect<int>>(R"({"value":42})") == conf::vt::indirect<int>(42));
580590
REQUIRE(test::deserialise<std::monostate>(R"({})") == std::monostate{});
@@ -584,7 +594,6 @@ TEST_CASE("Json reader can read std types from underlying text representation",
584594
REQUIRE(test::deserialise<std::string>(R"("Hello")") == std::string("Hello"));
585595
REQUIRE(test::deserialise<std::tuple<int, bool, std::string>>(R"([75,true,"Example"])") == std::tuple<int, bool, std::string>{75, true, "Example"});
586596
// REQUIRE(test::deserialise<std::variant<int, bool, std::string>>(R"({"type":"bool","value":true})") == std::variant<int, bool, std::string>{true});
587-
REQUIRE(test::deserialise<std::vector<int>>(R"([1, 2, 3, 4, 5])") == std::vector<int>{1, 2, 3, 4, 5});
588597
REQUIRE(*test::deserialise<std::unique_ptr<int>>(R"(50)") == 50);
589598
}
590599

libraries/core/tests/serialisation/json_writer.tests.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "morpheus/core/serialisation/adapters/std/bitset.hpp"
55
#include "morpheus/core/serialisation/adapters/std/chrono.hpp"
66
#include "morpheus/core/serialisation/adapters/std/indirect.hpp"
7+
#include "morpheus/core/serialisation/adapters/std/map.hpp"
78
#include "morpheus/core/serialisation/adapters/std/monostate.hpp"
89
#include "morpheus/core/serialisation/adapters/std/optional.hpp"
910
#include "morpheus/core/serialisation/adapters/std/pair.hpp"
@@ -412,6 +413,15 @@ TEST_CASE("Json writer can write std types to underlying text representation", "
412413
REQUIRE(test::serialise(std::chrono::years{100}) == R"("100y")");
413414
REQUIRE(test::serialise(std::chrono::months{12}) == R"("12m")");
414415
}
416+
SECTION("Container types")
417+
{
418+
REQUIRE(test::serialise(std::map<int, std::string>{
419+
{1, "a"},
420+
{2, "b"},
421+
{3, "b"}
422+
}) == R"([[1,"a"],[2,"b"],[3,"b"]])");
423+
REQUIRE(test::serialise(std::vector<int>{1, 2, 3, 4, 5}) == R"([1,2,3,4,5])");
424+
}
415425
REQUIRE(test::serialise(std::bitset<4>("1101")) == R"("1101")");
416426
REQUIRE(test::serialise(conf::vt::indirect<int>(42)) == R"({"value":42})");
417427
REQUIRE(test::serialise(std::monostate{}) == R"({})");
@@ -422,7 +432,6 @@ TEST_CASE("Json writer can write std types to underlying text representation", "
422432
REQUIRE(test::serialise(std::tuple<int, bool, std::string>{75, true, "Example"}) == R"([75,true,"Example"])");
423433
REQUIRE(test::serialise(std::make_unique<int>(123)) == R"(123)");
424434
REQUIRE(test::serialise(std::variant<int, bool, std::string>{true}) == R"({"type":"bool","value":true})");
425-
REQUIRE(test::serialise(std::vector<int>{1, 2, 3, 4, 5}) == R"([1,2,3,4,5])");
426435
}
427436

428437
TEST_CASE("Json writer can write ranges of composites", "[morpheus.serialisation.range.serialise.composites]")

0 commit comments

Comments
 (0)