make visitor + tests

main
Brett 2025-04-25 00:21:40 -04:00
parent fdc1fbf3ca
commit 5aacd7df9d
3 changed files with 110 additions and 81 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake) include(cmake/color.cmake)
set(BLT_VERSION 5.4.11) set(BLT_VERSION 5.4.12)
set(BLT_TARGET BLT) set(BLT_TARGET BLT)

View File

@ -24,7 +24,6 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <variant> #include <variant>
#include <blt/logging/logging.h>
#include <blt/meta/function.h> #include <blt/meta/function.h>
#include <blt/meta/passthrough.h> #include <blt/meta/passthrough.h>
#include <blt/meta/tuple_filters.h> #include <blt/meta/tuple_filters.h>
@ -32,6 +31,8 @@
namespace blt namespace blt
{ {
// TODO rewrite all of the metaprogramming related to this + the meta programming lib
template <typename... Types> template <typename... Types>
class variant_t; class variant_t;
@ -207,10 +208,61 @@ namespace blt
return m_variant.valueless_by_exception(); return m_variant.valueless_by_exception();
} }
template <typename T> template<typename... Visitee>
constexpr auto visit(T&& visitor) -> decltype(auto) static constexpr auto make_visitor(Visitee&& visitees)
{ {
return std::visit(std::forward<T>(visitor), m_variant); // TODO: this is probably not the best way to handle these cases...
using meta_t = detail::visit_return_type<std::tuple<Visitee...>, std::tuple<Types...>>;
if constexpr (meta_t::all_same)
{
if constexpr (meta_t::some_void)
{
return lambda_visitor{
[&](std::tuple_element_t<0, typename meta::function_like<Visitee>::args_tuple> value) {
if constexpr (std::is_void_v<typename meta::function_like<decltype(visitees)>::return_type>)
{
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value));
return typename meta_t::return_type{};
} else
{
return typename meta_t::return_type(
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value)));
}
}...
};
} else
{
return lambda_visitor{std::forward<Visitee>(visitees)...};
}
} else
{
if constexpr (meta_t::some_void)
{
return lambda_visitor{
[&](std::tuple_element_t<0, typename meta::function_like<Visitee>::args_tuple> value) {
if constexpr (std::is_void_v<typename meta::function_like<decltype(visitees)>::return_type>)
{
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value));
return typename meta_t::return_type{};
} else
{
return typename meta_t::return_type(
typename meta_t::base_type(
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value))));
}
}...
};
} else
{
return lambda_visitor{
[&](std::tuple_element_t<0, typename meta::function_like<Visitee>::args_tuple> value) {
return typename meta_t::return_type{
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value))
};
}...
};
}
}
} }
/** /**
@ -220,61 +272,7 @@ namespace blt
template <typename... Visitee> template <typename... Visitee>
constexpr auto visit(Visitee&&... visitees) -> decltype(auto) constexpr auto visit(Visitee&&... visitees) -> decltype(auto)
{ {
using meta_t = detail::visit_return_type<std::tuple<Visitee...>, std::tuple<Types...>>; return std::visit(make_visitor(std::forward<Visitee>(visitees)...), m_variant);
// BLT_TRACE(blt::type_string<typename meta_t::return_tuple>());
BLT_TRACE(blt::type_string<typename meta_t::non_void_returns>());
BLT_TRACE(blt::type_string<typename meta_t::same_type>());
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<Visitee>::args_tuple> value) {
if constexpr (std::is_void_v<typename meta::function_like<decltype(visitees)>::return_type>)
{
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value));
return typename meta_t::return_type{};
} else
{
return typename meta_t::return_type(
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value))
);
}
}...
}, m_variant);
} else
{
return std::visit(lambda_visitor{std::forward<Visitee>(visitees)...}, m_variant);
}
} else
{
if constexpr (meta_t::some_void)
{
return std::visit(lambda_visitor{
[&](std::tuple_element_t<0, typename meta::function_like<Visitee>::args_tuple> value) {
if constexpr (std::is_void_v<typename meta::function_like<decltype(visitees)>::return_type>)
{
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value));
return typename meta_t::return_type{};
} else
{
return typename meta_t::return_type(
typename meta_t::base_type(std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value)))
);
}
}...
}, m_variant);
} else
{
return std::visit(lambda_visitor{
[&](std::tuple_element_t<0, typename meta::function_like<Visitee>::args_tuple> value) {
return typename meta_t::return_type{
std::forward<Visitee>(visitees)(std::forward<decltype(value)>(value))
};
}...
}, m_variant);
}
}
} }
template <typename Default, typename... Visitee> template <typename Default, typename... Visitee>

View File

@ -45,7 +45,6 @@ struct type1 final : base_type
} }
}; };
struct type2 final : base_type struct type2 final : base_type
{ {
[[nodiscard]] int simple() const final // NOLINT [[nodiscard]] int simple() const final // NOLINT
@ -59,7 +58,6 @@ struct type2 final : base_type
} }
}; };
struct type3 final : base_type struct type3 final : base_type
{ {
[[nodiscard]] int simple() const final // NOLINT [[nodiscard]] int simple() const final // NOLINT
@ -76,8 +74,7 @@ struct type3 final : base_type
struct storing_type1 final : mutate_type struct storing_type1 final : mutate_type
{ {
explicit storing_type1(const int i): internal(i) explicit storing_type1(const int i): internal(i)
{ {}
}
[[nodiscard]] int simple() const final // NOLINT [[nodiscard]] int simple() const final // NOLINT
{ {
@ -100,8 +97,7 @@ struct storing_type1 final : mutate_type
struct storing_type2 final : mutate_type struct storing_type2 final : mutate_type
{ {
explicit storing_type2(const float i): internal(i * 2.2534f) explicit storing_type2(const float i): internal(i * 2.2534f)
{ {}
}
[[nodiscard]] int simple() const final // NOLINT [[nodiscard]] int simple() const final // NOLINT
{ {
@ -136,17 +132,17 @@ int main()
auto v1_result = v1.call_member(&base_type::to_string); 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(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<decltype(v1_result), std::string>, "Result type expected to be string!");
BLT_TRACE("V1: {}", v1_result); BLT_TRACE("V1: {}", v1_result);
auto v2_result = v2.call_member(&base_type::to_string); 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(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<decltype(v2_result), std::string>, "Result type expected to be string!");
BLT_TRACE("V2: {}", v2_result); BLT_TRACE("V2: {}", v2_result);
auto v3_result = v3.call_member(&base_type::to_string); 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(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<decltype(v3_result), std::string>, "Result type expected to be string!");
BLT_TRACE("V3: {}", v3.call_member(&base_type::to_string)); BLT_TRACE("V3: {}", v3.call_member(&base_type::to_string));
blt::variant_t<type1, type2, no_members> member_missing_stored_member{type1{}}; blt::variant_t<type1, type2, no_members> 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 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); auto no_member_result = member_missing_stored_no_member.call_member(&base_type::to_string);
BLT_ASSERT(typeid(stored_member_result) == typeid(std::optional<std::string>)); static_assert(std::is_same_v<decltype(stored_member_result), std::optional<std::string>>);
BLT_ASSERT(stored_member_result.has_value()); BLT_ASSERT(stored_member_result.has_value());
BLT_ASSERT(typeid(no_member_result) == typeid(std::optional<std::string>)); static_assert(std::is_same_v<decltype(no_member_result), std::optional<std::string>>);
BLT_ASSERT(!no_member_result.has_value()); BLT_ASSERT(!no_member_result.has_value());
BLT_TRACE("Stored: has value? '{}' value: '{}'", stored_member_result.has_value(), *stored_member_result); BLT_TRACE("Stored: has value? '{}' value: '{}'", stored_member_result.has_value(), *stored_member_result);
@ -167,28 +163,63 @@ int main()
return t1.simple(); return t1.simple();
}, [](const type2& t2) { }, [](const type2& t2) {
return t2.simple(); return t2.simple();
}, [](const type3&) { }, [](const type3&) {});
}); static_assert(std::is_same_v<decltype(visit_result_v1), std::optional<int>>, "Result type expected to be optional<int>!");
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) { auto visit_result_v2 = v2.visit([](const type1& t1) {
return static_cast<float>(t1.simple()); return static_cast<float>(t1.simple());
}, [](const type2& t2) { }, [](const type2& t2) {
return std::to_string(t2.simple()); return std::to_string(t2.simple());
}, [] (const type3& t3) { }, [](const type3& t3) {
return t3.simple(); return t3.simple();
}); });
auto visit_result_v3 = v2.visit([](const type1&) { static_assert(std::is_same_v<decltype(visit_result_v2), blt::variant_t<int, std::string, float>>,
}, [](const type2& t2) { "Result type expected to be variant_t<int, std::string, float>!");
BLT_ASSERT(visit_result_v2.index() == 1);
BLT_ASSERT(visit_result_v2.get<std::string>() == "2");
BLT_TRACE("Visit variant result: {}", visit_result_v2.get<std::string>());
auto visit_result_v3 = v2.visit([](const type1&) {}, [](const type2& t2) {
return std::to_string(t2.simple()); return std::to_string(t2.simple());
}, [] (const type3& t3) { }, [](const type3& t3) {
return t3.simple(); return t3.simple();
}); });
if (visit_result_v1) static_assert(std::is_same_v<decltype(visit_result_v3), std::optional<blt::variant_t<int, std::string>>>,
BLT_TRACE("Visit result: {}", *visit_result_v1); "Result type expected to be optional<variant_t<int, std::string>>!");
BLT_ASSERT(visit_result_v3.has_value());
BLT_ASSERT(visit_result_v3.value().index() == 1);
BLT_ASSERT(visit_result_v3.value().get<std::string>() == "2");
BLT_TRACE("Visit optional variant result: {}", visit_result_v3.value().get<std::string>());
BLT_TRACE(blt::type_string<decltype(visit_result_v1)>()); auto single_visitee = v3.visit([](auto&& a) {
return a.to_string();
});
static_assert(std::is_same_v<decltype(single_visitee), std::string>, "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<decltype(provided_visitor), std::string>, "Result type expected to be string!");
BLT_ASSERT(provided_visitor == type3{}.to_string());
BLT_TRACE("Provided visitor: {}", provided_visitor);
BLT_INFO("Variant tests passed!");
} }