make visitor + tests
parent
fdc1fbf3ca
commit
5aacd7df9d
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,9 +163,12 @@ 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());
|
||||||
|
@ -179,16 +178,48 @@ int main()
|
||||||
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!");
|
||||||
}
|
}
|
Loading…
Reference in New Issue