diff --git a/include/Support/Enum.h b/include/Support/Enum.h index f0f240b8..35d339ed 100644 --- a/include/Support/Enum.h +++ b/include/Support/Enum.h @@ -63,6 +63,9 @@ constexpr std::string_view enum_name(E value) { template class Enum { public: + /// Tag to indicate this is a special enum. + constexpr inline static bool is_special_enum_v = true; + /// A integral must explicitly convert to the enum. explicit constexpr Enum(underlying value) : m_Value(value) {} @@ -95,8 +98,6 @@ public: constexpr friend bool operator== (Enum lhs, Enum rhs) = default; - constexpr inline static bool is_enum = true; - constexpr static auto& all() { return enum_table::table; } @@ -133,6 +134,9 @@ private: template class Enum { public: + /// Tag to indicate this is a special enum. + constexpr inline static bool is_special_enum_v = true; + /// FIXME: Enum() = default; @@ -179,8 +183,6 @@ public: return masks; } - constexpr inline static bool is_enum = true; - constexpr static auto& all() { return enum_table::table; } @@ -239,9 +241,9 @@ private: }; template -constexpr bool is_enum_v = requires { - T::is_enum; - requires T::is_enum; +concept special_enum = requires { + T::is_special_enum_v; + requires T::is_special_enum_v; }; } // namespace clice::support @@ -249,7 +251,7 @@ constexpr bool is_enum_v = requires { namespace clice::json { template - requires support::is_enum_v + requires support::special_enum struct Serde { static json::Value serialize(const E& e) { return json::Value(e.value()); @@ -264,7 +266,7 @@ struct Serde { } // namespace clice::json template - requires clice::support::is_enum_v + requires clice::support::special_enum struct std::formatter : std::formatter { using Base = std::formatter; diff --git a/include/Support/JSON.h b/include/Support/JSON.h index 3826babe..3f2cca47 100644 --- a/include/Support/JSON.h +++ b/include/Support/JSON.h @@ -20,8 +20,8 @@ struct Serde; /// Check if the serde if given type is stateful. template concept stateful_serde = requires { - Serde::state; - requires Serde::state; + Serde::stateful; + requires Serde::stateful; }; /// Serialize an object to a JSON value. @@ -40,10 +40,13 @@ json::Value serialize(const V& v, Serdes&&... serdes) { } else if constexpr(sizeof...(rest) > 0) { /// Try the next serde. return self(self, std::forward(rest)...); + } else { + static_assert(dependent_false, "Unexpected control flow"); } }; return try_each(try_each, std::forward(serdes)...); } else { + /// Otherwise, pass the serdes to the next serde. return Serde::serialize(v, std::forward(serdes)...); } } else { @@ -67,6 +70,8 @@ T deserialize(const json::Value& value, Serdes&&... serdes) { } else if constexpr(sizeof...(rest) > 0) { /// Try the next serde. return self(self, std::forward(rest)...); + } else { + static_assert(dependent_false, "Unexpected control flow"); } }; return try_each(try_each, std::forward(serdes)...); @@ -74,7 +79,6 @@ T deserialize(const json::Value& value, Serdes&&... serdes) { /// Otherwise, pass the serdes to the next serde. return Serde::deserialize(value, std::forward(serdes)...); } - } else { static_assert(dependent_false, "Stateful Serde requires at least one serde"); } @@ -199,7 +203,7 @@ template struct Serde> { using V = std::array; - constexpr inline static bool state = stateful_serde; + constexpr inline static bool stateful = stateful_serde; template static json::Value serialize(const V& v, Serdes&&... serdes) { @@ -226,7 +230,7 @@ template struct Serde> { using V = std::vector; - constexpr inline static bool state = stateful_serde; + constexpr inline static bool stateful = stateful_serde; template static json::Value serialize(const V& v, Serdes&&... serdes) { @@ -252,7 +256,7 @@ template struct Serde> { using V = llvm::ArrayRef; - constexpr inline static bool state = stateful_serde; + constexpr inline static bool stateful = stateful_serde; /// Only support serialization. template diff --git a/include/Support/Struct.h b/include/Support/Struct.h index 813416b4..8ed12072 100644 --- a/include/Support/Struct.h +++ b/include/Support/Struct.h @@ -53,18 +53,22 @@ consteval auto member_name() { #if __GNUC__ && (!__clang__) && (!_MSC_VER) std::size_t start = name.rfind("::") + 2; std::size_t end = name.rfind(')'); - return name.substr(start, end - start); + name = name.substr(start, end - start); #elif __clang__ std::size_t start = name.rfind(".") + 1; std::size_t end = name.rfind('}'); - return name.substr(start, end - start); + name = name.substr(start, end - start); #elif _MSC_VER std::size_t start = name.rfind("->") + 2; std::size_t end = name.rfind('}'); - return name.substr(start, end - start); + name = name.substr(start, end - start); #else static_assert(false, "Not supported compiler"); #endif + if(name.rfind("::") != std::string_view::npos) { + name = name.substr(name.rfind("::") + 2); + } + return name; } } // namespace impl @@ -190,6 +194,37 @@ struct Struct { template concept reflectable = Struct::reflectable; +template +struct Inheritance : Ts... {}; + +/// Use to define a reflectable struct with inheritance. +#define inherited_struct(name, ...) \ + struct name##Body; \ + using name = clice::support::Inheritance<__VA_ARGS__, name##Body>; \ + struct name##Body + +template +struct Struct> { + constexpr inline static bool reflectable = (support::reflectable && ...); + + constexpr static std::size_t member_count() { + return (Struct::member_count() + ...); + } + + constexpr static auto& instance() { + return impl::storage>::value; + } + + template + constexpr static auto collcet_members(Object&& object) { + return std::tuple_cat(Struct::collcet_members(static_cast(object))...); + } +}; + +template +using member_types = + tuple_to_list_t::collcet_members(std::declval())), std::remove_pointer_t>; + /// Turn the return value of the callable to bool. template constexpr auto foldable(const Callable& callable) { @@ -205,13 +240,13 @@ constexpr auto foldable(const Callable& callable) { } template -constexpr bool foreach(Object&& object, const Callback& callable) { +constexpr bool foreach(Object&& object, const Callback& callback) { using S = Struct>; constexpr auto count = S::member_count(); auto members = S::collcet_members(object); constexpr auto static_members = S::collcet_members(S::instance()); return [&](std::index_sequence) { - return (foldable(callable)(impl::member_name(static_members)>(), + return (foldable(callback)(impl::member_name(static_members)>(), *std::get(members)) && ...); }(std::make_index_sequence{}); @@ -237,10 +272,8 @@ namespace clice::json { template struct Serde { - constexpr inline static bool state = - !support::foreach(support::Struct::instance(), [](auto, auto&& member) { - return !json::stateful_serde>; - }); + constexpr inline static bool stateful = + support::member_types::apply([] { return (stateful_serde || ...); }); template static json::Value serialize(const T& t, Serdes&&... serdes) { @@ -270,18 +303,3 @@ struct Serde { } // namespace clice::json -/// FIXME: -// template -// struct std::formatter : std::formatter { -// using Base = std::formatter; -// -// template -// constexpr auto parse(ParseContext& ctx) { -// return Base::parse(ctx); -// } -// -// template -// auto format(const T& t, FormatContext& ctx) { -// return Base::format(clice::json::serialize(t), ctx); -// } -// }; diff --git a/include/Support/TypeTraits.h b/include/Support/TypeTraits.h index 9424dc8b..f0811779 100644 --- a/include/Support/TypeTraits.h +++ b/include/Support/TypeTraits.h @@ -33,23 +33,14 @@ struct replace_cv_ref { template using replace_cv_ref_t = typename replace_cv_ref::type; -template typename Map> -struct tuple_map; - -template typename Map> -struct tuple_map, Map> { - using type = std::tuple::type...>; -}; - -/// Map the types in the tuple to another type with the given template template argument. -template typename Map> -using tuple_map_t = typename tuple_map::type; - template struct identity { using type = T; }; +template +using identity_t = T; + template typename HKT> constexpr bool is_specialization_of = false; @@ -64,7 +55,34 @@ concept integral = std::is_integral_v && !std::is_same_v && !std::is_same_v && !std::is_same_v && !std::is_same_v && !std::is_same_v; -template +template concept floating_point = std::is_floating_point_v; +template +struct type_list { + constexpr static auto apply(auto&& lambda) { + return lambda.template operator()(); + } +}; + +/// Turn a tuple into a type list. +/// @param Tuple The tuple to convert. +/// @param Map The mapping function to apply to each type in the tuple. +/// @param isalias If isalias is false, mapping result is `typename Map::type`. +template typename Map = identity_t, bool isalias = true> +struct tuple_to_list; + +template typename Map> +struct tuple_to_list, Map, true> { + using type = type_list...>; +}; + +template typename Map> +struct tuple_to_list, Map, false> { + using type = type_list::type...>; +}; + +template typename Map = identity_t, bool isalias = true> +using tuple_to_list_t = typename tuple_to_list::type; + } // namespace clice diff --git a/unittests/Support/Enum.cpp b/unittests/Support/Enum.cpp index adce700d..7861f2e0 100644 --- a/unittests/Support/Enum.cpp +++ b/unittests/Support/Enum.cpp @@ -1,22 +1,21 @@ -#include "../Test.h" -#include +#include "Test.h" + +namespace clice { namespace { -using namespace clice; +TEST(Support, NormalEnum) { + struct Color : support::Enum { + using Enum::Enum; -struct Color : support::Enum { - using Enum::Enum; - - enum Kind : uint8_t { - Red, - Green, - Blue, - Yellow, + enum Kind : uint8_t { + Red, + Green, + Blue, + Yellow, + }; }; -}; -TEST(Support, normal_enum) { constexpr Color red = Color::Red; constexpr Color green = Color::Green; constexpr Color blue = Color::Blue; @@ -33,18 +32,18 @@ TEST(Support, normal_enum) { static_assert(red == red2); } -struct Mask : support::Enum { - using Enum::Enum; +TEST(Support, MaskEnum) { + struct Mask : support::Enum { + using Enum::Enum; - enum Kind { - A = 0, - B, - C, - D, + enum Kind { + A = 0, + B, + C, + D, + }; }; -}; -TEST(Support, mask_enum) { constexpr Mask mask = Mask::A; constexpr Mask mask2 = Mask::B; constexpr Mask mask3 = Mask::C; @@ -59,9 +58,21 @@ TEST(Support, mask_enum) { constexpr Mask mask5 = Mask(Mask::A, Mask::B, Mask::C, Mask::D); static_assert(mask5.name() == "A | B | C | D" && mask5.value() == 15); + + Mask mask6 = Mask::A; + mask6 |= Mask::B; + EXPECT_EQ(mask6.name(), "A | B"); + EXPECT_TRUE(bool(mask6 & Mask::A)); + EXPECT_TRUE(bool(mask6 & Mask::B)); + + mask6 |= Mask::C; + EXPECT_EQ(mask6.name(), "A | B | C"); + EXPECT_TRUE(bool(mask6 & Mask::A)); + EXPECT_TRUE(bool(mask6 & Mask::B)); + EXPECT_TRUE(bool(mask6 & Mask::C)); } - - } // namespace +} // namespace clice + diff --git a/unittests/Support/JSON.cpp b/unittests/Support/JSON.cpp index e8f46fc9..a3ffe883 100644 --- a/unittests/Support/JSON.cpp +++ b/unittests/Support/JSON.cpp @@ -1,57 +1,82 @@ -#include -#include +#include "Test.h" namespace clice { +namespace { +struct ValueRef { + std::size_t index; +}; +} // namespace + +template <> +struct json::Serde { + constexpr inline static bool stateful = true; + + std::vector& encoder; + std::vector& decoder; + + json::Value serialize(const ValueRef& ref) { + return json::Value(decoder[ref.index]); + } + + ValueRef deserialize(const json::Value& value) { + encoder.emplace_back(*value.getAsInteger()); + return ValueRef{encoder.size() - 1}; + } +}; + namespace { TEST(Support, JSON) { - json::Object object; - object["x"] = 1; - object["y"] = 2; - struct Point { int x; int y; }; + json::Value object = json::Object{ + {"x", 1}, + {"y", 2}, + }; + auto point = clice::json::deserialize(std::move(object)); ASSERT_EQ(point.x, 1); ASSERT_EQ(point.y, 2); - // auto result = clice::json::serialize(point); - // ASSERT_EQ(result, json::Value(std::move(object))); + auto result = clice::json::serialize(point); + ASSERT_EQ(result, object); +} + +TEST(Support, StatefulSerde) { + struct Refs { + std::vector data; + }; + + static_assert(json::stateful_serde); + + Refs refs; + refs.data.emplace_back(4); + refs.data.emplace_back(3); + refs.data.emplace_back(2); + refs.data.emplace_back(1); + refs.data.emplace_back(0); + + std::vector encoder; + std::vector decoder = {1, 2, 3, 4, 5}; + json::Serde serde{encoder, decoder}; + + auto result = json::serialize(refs, serde); + ASSERT_EQ(result, + json::Value(json::Object{ + {"data", {5, 4, 3, 2, 1}} + })); + + auto refs2 = json::deserialize(result, serde); + ASSERT_EQ(refs2.data.size(), 5); + for(std::size_t i = 0; i < refs2.data.size(); ++i) { + ASSERT_EQ(refs2.data[i].index, i); + } } } // namespace -struct ValueRef { - int index; -}; - -template <> -struct json::Serde { - constexpr inline static bool state = true; - - std::vector& decoder; - - json::Value serialize(const ValueRef& ref) { - return json::Value(decoder[ref.index]); - } -}; - -TEST(Support, StatefulSerde) { - std::vector refs; - refs.emplace_back(4); - refs.emplace_back(3); - refs.emplace_back(2); - refs.emplace_back(1); - refs.emplace_back(0); - - std::vector decoder = {1, 2, 3, 4, 5}; - json::Serde serde{decoder}; - auto result = json::serialize(refs, serde); - ASSERT_EQ(result, json::Value(json::Array{5, 4, 3, 2, 1})); -} - } // namespace clice diff --git a/unittests/Support/Struct.cpp b/unittests/Support/Struct.cpp new file mode 100644 index 00000000..bbe75c44 --- /dev/null +++ b/unittests/Support/Struct.cpp @@ -0,0 +1,64 @@ +#include "Test.h" + +namespace clice { + +namespace { + +struct X { + int x; + int y; +}; + +static_assert(std::is_same_v, type_list>); + +TEST(Support, Struct) { + bool x = false, y = false; + support::foreach(X{1, 2}, [&](auto name, auto value) { + if(name == "x") { + x = true; + EXPECT_EQ(value, 1); + } else if(name == "y") { + y = true; + EXPECT_EQ(value, 2); + } else { + EXPECT_TRUE(false); + } + }); + EXPECT_TRUE(x && y); + + struct X x1 = {1, 2}; + struct X x2 = {3, 4}; + EXPECT_TRUE(support::foreach(x1, x2, [](auto& lhs, auto& rhs) { return lhs = rhs; })); + EXPECT_EQ(x1.x, 3); + EXPECT_EQ(x1.y, 4); +} + +inherited_struct(Y, X) { + int z; +}; + +static_assert(std::is_same_v, type_list>); + +TEST(Support, Inheritance) { + bool x = false, y = false, z = false; + support::foreach(Y{1, 2, 3}, [&](auto name, auto value) { + if(name == "x") { + x = true; + EXPECT_EQ(value, 1); + } else if(name == "y") { + y = true; + EXPECT_EQ(value, 2); + } else if(name == "z") { + z = true; + EXPECT_EQ(value, 3); + } else { + EXPECT_TRUE(false); + } + }); + EXPECT_TRUE(x && y && z); +} + +} // namespace + +} // namespace clice + diff --git a/unittests/Support/Test.h b/unittests/Support/Test.h new file mode 100644 index 00000000..ee059a95 --- /dev/null +++ b/unittests/Support/Test.h @@ -0,0 +1,4 @@ +#pragma once + +#include "../Test.h" +#include "Support/Support.h" diff --git a/unittests/Support/TokenBuffer.cpp b/unittests/Support/TokenBuffer.cpp deleted file mode 100644 index f733c9b7..00000000 --- a/unittests/Support/TokenBuffer.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -TEST(clang, TokenBuffer) {}