diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a3b96a..13f9e82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) include(cmake/color.cmake) -set(BLT_VERSION 5.4.11) +set(BLT_VERSION 5.4.12) set(BLT_TARGET BLT) diff --git a/include/blt/std/variant.h b/include/blt/std/variant.h index c100d26..e4dfefd 100644 --- a/include/blt/std/variant.h +++ b/include/blt/std/variant.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -32,6 +31,8 @@ namespace blt { + // TODO rewrite all of the metaprogramming related to this + the meta programming lib + template class variant_t; @@ -207,10 +208,61 @@ namespace blt return m_variant.valueless_by_exception(); } - template - constexpr auto visit(T&& visitor) -> decltype(auto) + template + static constexpr auto make_visitor(Visitee&& visitees) { - return std::visit(std::forward(visitor), m_variant); + // TODO: this is probably not the best way to handle these cases... + using meta_t = detail::visit_return_type, std::tuple>; + if constexpr (meta_t::all_same) + { + if constexpr (meta_t::some_void) + { + return lambda_visitor{ + [&](std::tuple_element_t<0, typename meta::function_like::args_tuple> value) { + if constexpr (std::is_void_v::return_type>) + { + std::forward(visitees)(std::forward(value)); + return typename meta_t::return_type{}; + } else + { + return typename meta_t::return_type( + std::forward(visitees)(std::forward(value))); + } + }... + }; + } else + { + return lambda_visitor{std::forward(visitees)...}; + } + } else + { + if constexpr (meta_t::some_void) + { + return lambda_visitor{ + [&](std::tuple_element_t<0, typename meta::function_like::args_tuple> value) { + if constexpr (std::is_void_v::return_type>) + { + std::forward(visitees)(std::forward(value)); + return typename meta_t::return_type{}; + } else + { + return typename meta_t::return_type( + typename meta_t::base_type( + std::forward(visitees)(std::forward(value)))); + } + }... + }; + } else + { + return lambda_visitor{ + [&](std::tuple_element_t<0, typename meta::function_like::args_tuple> value) { + return typename meta_t::return_type{ + std::forward(visitees)(std::forward(value)) + }; + }... + }; + } + } } /** @@ -220,61 +272,7 @@ namespace blt template constexpr auto visit(Visitee&&... visitees) -> decltype(auto) { - using meta_t = detail::visit_return_type, std::tuple>; - // BLT_TRACE(blt::type_string()); - BLT_TRACE(blt::type_string()); - BLT_TRACE(blt::type_string()); - if constexpr (meta_t::all_same) - { - if constexpr (meta_t::some_void) - { - return std::visit(lambda_visitor{ - [&](std::tuple_element_t<0, typename meta::function_like::args_tuple> value) { - if constexpr (std::is_void_v::return_type>) - { - std::forward(visitees)(std::forward(value)); - return typename meta_t::return_type{}; - } else - { - return typename meta_t::return_type( - std::forward(visitees)(std::forward(value)) - ); - } - }... - }, m_variant); - } else - { - return std::visit(lambda_visitor{std::forward(visitees)...}, m_variant); - } - } else - { - if constexpr (meta_t::some_void) - { - return std::visit(lambda_visitor{ - [&](std::tuple_element_t<0, typename meta::function_like::args_tuple> value) { - if constexpr (std::is_void_v::return_type>) - { - std::forward(visitees)(std::forward(value)); - return typename meta_t::return_type{}; - } else - { - return typename meta_t::return_type( - typename meta_t::base_type(std::forward(visitees)(std::forward(value))) - ); - } - }... - }, m_variant); - } else - { - return std::visit(lambda_visitor{ - [&](std::tuple_element_t<0, typename meta::function_like::args_tuple> value) { - return typename meta_t::return_type{ - std::forward(visitees)(std::forward(value)) - }; - }... - }, m_variant); - } - } + return std::visit(make_visitor(std::forward(visitees)...), m_variant); } template diff --git a/tests/variant_tests.cpp b/tests/variant_tests.cpp index 8aed3d0..0d8917f 100644 --- a/tests/variant_tests.cpp +++ b/tests/variant_tests.cpp @@ -45,7 +45,6 @@ struct type1 final : base_type } }; - struct type2 final : base_type { [[nodiscard]] int simple() const final // NOLINT @@ -59,7 +58,6 @@ struct type2 final : base_type } }; - struct type3 final : base_type { [[nodiscard]] int simple() const final // NOLINT @@ -76,8 +74,7 @@ struct type3 final : base_type struct storing_type1 final : mutate_type { explicit storing_type1(const int i): internal(i) - { - } + {} [[nodiscard]] int simple() const final // NOLINT { @@ -100,8 +97,7 @@ struct storing_type1 final : mutate_type struct storing_type2 final : mutate_type { explicit storing_type2(const float i): internal(i * 2.2534f) - { - } + {} [[nodiscard]] int simple() const final // NOLINT { @@ -136,17 +132,17 @@ int main() auto v1_result = v1.call_member(&base_type::to_string); BLT_ASSERT_MSG(v1_result == type1{}.to_string(), ("Expected result to be " + type1{}.to_string() + " but found " + v1_result).c_str()); - BLT_ASSERT_MSG(typeid(v1_result) == typeid(std::string), "Result type expected to be string!"); + static_assert(std::is_same_v, "Result type expected to be string!"); BLT_TRACE("V1: {}", v1_result); auto v2_result = v2.call_member(&base_type::to_string); BLT_ASSERT_MSG(v2_result == type2{}.to_string(), ("Expected result to be " + type2{}.to_string() + " but found " + v2_result).c_str()); - BLT_ASSERT_MSG(typeid(v2_result) == typeid(std::string), "Result type expected to be string!"); + static_assert(std::is_same_v, "Result type expected to be string!"); BLT_TRACE("V2: {}", v2_result); auto v3_result = v3.call_member(&base_type::to_string); BLT_ASSERT_MSG(v3_result == type3{}.to_string(), ("Expected result to be " + type3{}.to_string() + " but found " + v3_result).c_str()); - BLT_ASSERT_MSG(typeid(v3_result) == typeid(std::string), "Result type expected to be string!"); + static_assert(std::is_same_v, "Result type expected to be string!"); BLT_TRACE("V3: {}", v3.call_member(&base_type::to_string)); blt::variant_t member_missing_stored_member{type1{}}; @@ -155,9 +151,9 @@ int main() auto stored_member_result = member_missing_stored_member.call_member(&base_type::to_string); auto no_member_result = member_missing_stored_no_member.call_member(&base_type::to_string); - BLT_ASSERT(typeid(stored_member_result) == typeid(std::optional)); + static_assert(std::is_same_v>); BLT_ASSERT(stored_member_result.has_value()); - BLT_ASSERT(typeid(no_member_result) == typeid(std::optional)); + static_assert(std::is_same_v>); BLT_ASSERT(!no_member_result.has_value()); BLT_TRACE("Stored: has value? '{}' value: '{}'", stored_member_result.has_value(), *stored_member_result); @@ -167,28 +163,63 @@ int main() return t1.simple(); }, [](const type2& t2) { return t2.simple(); - }, [](const type3&) { + }, [](const type3&) {}); - }); + static_assert(std::is_same_v>, "Result type expected to be optional!"); + BLT_ASSERT(visit_result_v1.has_value()); + BLT_ASSERT(*visit_result_v1 == 1); + BLT_TRACE("Visit optional int: {}", *visit_result_v1); auto visit_result_v2 = v2.visit([](const type1& t1) { return static_cast(t1.simple()); }, [](const type2& t2) { return std::to_string(t2.simple()); - }, [] (const type3& t3) { + }, [](const type3& t3) { return t3.simple(); }); - auto visit_result_v3 = v2.visit([](const type1&) { - }, [](const type2& t2) { + static_assert(std::is_same_v>, + "Result type expected to be variant_t!"); + BLT_ASSERT(visit_result_v2.index() == 1); + BLT_ASSERT(visit_result_v2.get() == "2"); + BLT_TRACE("Visit variant result: {}", visit_result_v2.get()); + + auto visit_result_v3 = v2.visit([](const type1&) {}, [](const type2& t2) { return std::to_string(t2.simple()); - }, [] (const type3& t3) { + }, [](const type3& t3) { return t3.simple(); }); - if (visit_result_v1) - BLT_TRACE("Visit result: {}", *visit_result_v1); + static_assert(std::is_same_v>>, + "Result type expected to be optional>!"); + BLT_ASSERT(visit_result_v3.has_value()); + BLT_ASSERT(visit_result_v3.value().index() == 1); + BLT_ASSERT(visit_result_v3.value().get() == "2"); + BLT_TRACE("Visit optional variant result: {}", visit_result_v3.value().get()); - BLT_TRACE(blt::type_string()); + auto single_visitee = v3.visit([](auto&& a) { + return a.to_string(); + }); -} \ No newline at end of file + static_assert(std::is_same_v, "Result type expected to be string!"); + BLT_ASSERT(single_visitee == type3{}.to_string()); + BLT_TRACE("Single visitee: {}", single_visitee); + + auto provided_visitor = v3.visit(blt::lambda_visitor{ + [](const type1& t1) { + return t1.to_string(); + }, + [](const type2& t2) { + return t2.to_string(); + }, + [](const type3& t3) { + return t3.to_string(); + } + }); + + static_assert(std::is_same_v, "Result type expected to be string!"); + BLT_ASSERT(provided_visitor == type3{}.to_string()); + BLT_TRACE("Provided visitor: {}", provided_visitor); + + BLT_INFO("Variant tests passed!"); +}