partial variant

main
Brett 2025-04-22 00:41:16 -04:00
parent f0e9475dcc
commit 8f3cbcb2fd
4 changed files with 583 additions and 477 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.5) set(BLT_VERSION 5.4.6)
set(BLT_TARGET BLT) set(BLT_TARGET BLT)
@ -238,6 +238,7 @@ if (${BUILD_TESTS})
blt_add_test(blt_iterator tests/iterator_tests.cpp test) blt_add_test(blt_iterator tests/iterator_tests.cpp test)
blt_add_test(blt_argparse tests/argparse_tests.cpp test) blt_add_test(blt_argparse tests/argparse_tests.cpp test)
blt_add_test(blt_logging tests/logger_tests.cpp test) blt_add_test(blt_logging tests/logger_tests.cpp test)
blt_add_test(blt_variant tests/variant_tests.cpp test)
message("Built tests") message("Built tests")
endif () endif ()

View File

@ -21,487 +21,457 @@
#include <functional> #include <functional>
#include <optional> #include <optional>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <variant> #include <variant>
#include <blt/std/types.h> #include <blt/std/types.h>
#include <tuple>
namespace blt namespace blt
{ {
template <typename... Types> template <typename... Types>
class variant_t; class variant_t;
namespace detail namespace detail
{ {
template <typename... Ts> template <typename... Ts>
struct filter_void; struct filter_void;
template <> template <>
struct filter_void<> struct filter_void<>
{ {
using type = std::tuple<>; using type = std::tuple<>;
}; };
template <typename T, typename... Ts> template <typename T, typename... Ts>
struct filter_void<T, Ts...> struct filter_void<T, Ts...>
{ {
using type = std::conditional_t< using type = std::conditional_t<std::is_same_v<T, void>, typename filter_void<Ts...>::type, decltype(std::tuple_cat(
std::is_same_v<T, void>, std::declval<std::tuple<T>>(), std::declval<typename filter_void<Ts...>::type>()))>;
typename filter_void<Ts...>::type, };
decltype(std::tuple_cat(
std::declval<std::tuple<T>>(), template <typename... Ts>
std::declval<typename filter_void<Ts...>::type>() using filter_void_t = typename filter_void<Ts...>::type;
))
>; template <typename Type, typename... Ts>
}; struct filter_invoke;
template <typename... Ts> template <typename Type>
using filter_void_t = typename filter_void<Ts...>::type; struct filter_invoke<Type>
{
template <typename Type, typename Func> using type = std::tuple<>;
struct member_func_meta };
{
using can_invoke = std::is_invocable<Func, Type>; template <typename Type, typename T, typename... Ts>
struct filter_invoke<Type, T, Ts...>
using return_type = std::conditional_t<can_invoke::value, std::invoke_result_t<Func, Type>, void>; {
}; using type = std::conditional_t<std::is_invocable_v<T, Type>, decltype(std::tuple_cat(
std::declval<std::tuple<T>>(), std::declval<typename filter_invoke<Ts...>::type>())),
template <typename T, typename Func, size_t Index, bool HasValue> typename filter_invoke<Ts...>::type>;
struct passthrough_value };
{
using type = T; template <typename... Ts>
using func = Func; using filter_invoke_t = typename filter_invoke<Ts...>::type;
constexpr static size_t index = Index;
template <typename Type, typename Func>
explicit operator bool() const struct member_func_meta
{ {
return HasValue; using can_invoke = std::is_invocable<Func, Type>;
}
}; using return_type = std::conditional_t<can_invoke::value, std::invoke_result_t<Func, Type>, void>;
};
template <typename Type, typename... Funcs>
struct first_invoke_member_func template <typename T, typename Func, size_t Index>
{ struct passthrough_value
template <size_t... Indexes> {
constexpr static auto find_func(std::index_sequence<Indexes...>) using type = T;
{ using func = Func;
return (... || []() constexpr static size_t index = Index;
{
using Meta = member_func_meta<Type, Funcs>; bool has_value;
if constexpr (Meta::can_invoke::value)
{ explicit passthrough_value(const bool has_value): has_value(has_value)
return passthrough_value<typename Meta::return_type, Funcs, Indexes, true>{}; {}
}
return passthrough_value<void, Funcs, Indexes, false>{}; explicit operator bool() const
}()); {
} return has_value;
}
using result = decltype(find_func(std::index_sequence_for<Funcs...>())); };
using return_type = typename result::type;
using func_type = typename result::func; template <typename Type, typename... Funcs>
constexpr static size_t function_index = result::index; struct first_invoke_member_func
}; {
template <size_t... Indexes>
template <typename FuncTuple, typename ArgTuple> constexpr static auto find_func(std::index_sequence<Indexes...>)
struct member_func_detail {
{ return (... || []() {
}; using Meta = member_func_meta<Type, Funcs>;
if constexpr (Meta::can_invoke::value)
template <typename... Funcs, typename... Args> {
struct member_func_detail<std::tuple<Funcs...>, std::tuple<Args...>> return passthrough_value<typename Meta::return_type, Funcs, Indexes>{true};
{ }
using result_types = std::tuple<first_invoke_member_func<Args, Funcs...>...>; return passthrough_value<typename Meta::return_type, Funcs, Indexes>{false};
using base_type = typename decltype(std::get<0>(std::declval<result_types>))::type; }());
}
template <typename T>
using get_type = typename T::type; using result = decltype(find_func(std::index_sequence_for<Funcs...>()));
using return_type = typename result::type;
template <typename T> using func_type = typename result::func;
using is_base = std::is_same<typename T::type, base_type>; constexpr static size_t function_index = result::index;
};
template <typename T>
using is_base_or_void = std::disjunction<std::is_void<typename T::type>, is_base<typename T::type>>; template <typename FuncTuple, typename ArgTuple>
struct member_func_detail;
template <template<typename...> typename Functor, template<typename> typename PerType, size_t... Indexes>
constexpr static auto for_each_type(std::index_sequence<Indexes...>) template <typename... Funcs, typename... Args>
{ struct member_func_detail<std::tuple<Funcs...>, std::tuple<Args...>>
return std::declval<Functor<PerType<decltype(std::get<Indexes>(std::declval<result_types>()))>...>>; {
} using result_types = std::tuple<first_invoke_member_func<Args, Funcs...>...>;
using base_type = typename std::tuple_element_t<0, result_types>::return_type;
constexpr static bool all_has_void = for_each_type<std::conjunction_v, std::is_void>(std::index_sequence_for<Args...>());
constexpr static bool all_has_ret = for_each_type<std::conjunction_v, is_base>(std::index_sequence_for<Args...>()); template <typename T>
constexpr static bool all_has_ret_or_void = for_each_type<std::conjunction_v, is_base_or_void>(std::index_sequence_for<Args...>()); using get_type = typename T::return_type;
using non_void_types = typename decltype(for_each_type<filter_void_t, get_type>(std::index_sequence_for<Args...>()))::type; template <typename T>
using is_base = std::is_same<T, base_type>;
template <size_t... Indexes>
static constexpr auto make_variant(std::index_sequence<Indexes...>) template <typename T>
{ using is_base_or_void = std::disjunction<std::is_void<typename T::return_type>, is_base<typename T::return_type>>;
if constexpr (all_has_void)
return; template <template<typename...> typename Functor, template<typename> typename PerType, size_t... Indexes>
using variant = variant_t<decltype(std::get<Indexes>(std::declval<non_void_types>()))...>; constexpr static auto for_each_type(std::index_sequence<Indexes...>)
return std::declval<variant>(); {
} return std::declval<Functor<PerType<std::tuple_element_t<Indexes, result_types>>...>>;
}
static constexpr auto make_return_type()
{ constexpr static bool all_has_void = std::decay_t<std::invoke_result_t<decltype(for_each_type<std::conjunction, std::is_void>(
if constexpr (all_has_void) std::index_sequence_for<Args...>()))>>::value;
return; constexpr static bool all_has_ret = std::decay_t<std::invoke_result_t<decltype(for_each_type<std::conjunction, is_base>(
if constexpr (all_has_ret) std::index_sequence_for<Args...>()))>>::value;
return std::declval<base_type>(); constexpr static bool all_has_ret_or_void = std::decay_t<std::invoke_result_t<decltype(for_each_type<std::conjunction, is_base_or_void>(
if constexpr (all_has_ret_or_void) std::index_sequence_for<Args...>()))>>::value;
return std::declval<std::optional<base_type>>();
return make_variant(std::make_index_sequence<std::tuple_size_v<non_void_types>>{}); using non_void_types = typename std::decay_t<std::invoke_result_t<decltype(for_each_type<filter_void, get_type>(
} std::index_sequence_for<Args...>()))>>::type;
};
} template <size_t... Indexes>
static constexpr auto make_variant(std::index_sequence<Indexes...>)
/* {
* std::visit(blt::lambda_visitor{ using variant = variant_t<std::decay_t<std::tuple_element_t<Indexes, non_void_types>>...>;
* lambdas... return std::declval<variant>();
* }, data_variant); }
*/
using make_return_type = std::conditional_t<all_has_void, void, std::conditional_t<
template <typename... TLambdas> all_has_ret, base_type, std::conditional_t<
struct lambda_visitor : TLambdas... all_has_ret_or_void, std::optional<base_type>, std::conditional_t<
{ std::tuple_size_v<non_void_types> == 0, void, decltype(make_variant(
using TLambdas::operator()...; std::make_index_sequence<std::tuple_size_v<non_void_types>>{}))>>>>;
}; };
}
#if __cplusplus < 202002L
/*
// explicit deduction guide (not needed as of C++20) * std::visit(blt::lambda_visitor{
template <typename... TLambdas> * lambdas...
lambda_visitor(TLambdas...) -> lambda_visitor<TLambdas...>; * }, data_variant);
*/
#endif
template <typename... TLambdas>
template <typename... Types> struct lambda_visitor : TLambdas...
class variant_t {
{ using TLambdas::operator()...;
public: };
using value_type = std::variant<Types...>;
size_t variant_size = sizeof...(Types); #if __cplusplus < 202002L
constexpr variant_t(): m_variant() // explicit deduction guide (not needed as of C++20)
{ template <typename... TLambdas>
} lambda_visitor(TLambdas...) -> lambda_visitor<TLambdas...>;
constexpr variant_t(const variant_t& variant) noexcept(std::is_nothrow_copy_constructible_v<value_type>): m_variant(variant.m_variant) #endif
{
} template <typename... Types>
class variant_t
constexpr variant_t(variant_t&& variant) noexcept(std::is_nothrow_move_constructible_v<value_type>): m_variant(std::move(variant.m_variant)) {
{ public:
} using value_type = std::variant<Types...>;
size_t variant_size = sizeof...(Types);
explicit constexpr variant_t(const value_type& variant) noexcept(std::is_nothrow_copy_constructible_v<value_type>): m_variant(variant)
{ constexpr variant_t(): m_variant()
} {}
explicit constexpr variant_t(value_type&& variant) noexcept(std::is_nothrow_move_constructible_v<value_type>): m_variant(std::move(variant)) constexpr variant_t(const variant_t& variant) noexcept(std::is_nothrow_copy_constructible_v<value_type>): m_variant(variant.m_variant)
{ {}
}
constexpr variant_t(variant_t&& variant) noexcept(std::is_nothrow_move_constructible_v<value_type>): m_variant(std::move(variant.m_variant))
explicit constexpr variant_t(Types&&... args) noexcept(std::is_nothrow_constructible_v<value_type, Types...>): m_variant( {}
std::forward<Types>(args)...)
{ explicit constexpr variant_t(const value_type& variant) noexcept(std::is_nothrow_copy_constructible_v<value_type>): m_variant(variant)
} {}
template <typename T, typename... C_Args> explicit constexpr variant_t(value_type&& variant) noexcept(std::is_nothrow_move_constructible_v<value_type>): m_variant(std::move(variant))
explicit constexpr variant_t(std::in_place_type_t<T>, C_Args&&... args): m_variant(std::in_place_type<T>, std::forward<C_Args>(args)...) {}
{
} explicit constexpr variant_t(Types&&... args) noexcept(std::is_nothrow_constructible_v<value_type, Types...>): m_variant(
std::forward<Types>(args)...)
template <typename T, typename U, typename... C_Args> {}
constexpr explicit variant_t(std::in_place_type_t<T>, std::initializer_list<U> il, C_Args&&... args): m_variant(
std::in_place_type<T>, il, std::forward<C_Args>(args)...) template <typename T, typename... C_Args>
{ explicit constexpr variant_t(std::in_place_type_t<T>, C_Args&&... args): m_variant(std::in_place_type<T>, std::forward<C_Args>(args)...)
} {}
template <size_t I, typename... C_Args> template <typename T, typename U, typename... C_Args>
explicit constexpr variant_t(std::in_place_index_t<I>, C_Args&&... args): m_variant(std::in_place_index<I>, std::forward<C_Args>(args)...) constexpr explicit variant_t(std::in_place_type_t<T>, std::initializer_list<U> il, C_Args&&... args): m_variant(
{ std::in_place_type<T>, il, std::forward<C_Args>(args)...)
} {}
template <std::size_t I, typename U, typename... C_Args> template <size_t I, typename... C_Args>
constexpr explicit variant_t(std::in_place_index_t<I>, std::initializer_list<U> il, C_Args&&... args): m_variant( explicit constexpr variant_t(std::in_place_index_t<I>, C_Args&&... args): m_variant(std::in_place_index<I>, std::forward<C_Args>(args)...)
std::in_place_index<I>, il, std::forward<C_Args>(args)...) {}
{
} template <std::size_t I, typename U, typename... C_Args>
constexpr explicit variant_t(std::in_place_index_t<I>, std::initializer_list<U> il, C_Args&&... args): m_variant(
template <typename T, typename... Args> std::in_place_index<I>, il, std::forward<C_Args>(args)...)
T& emplace(Args&&... args) {}
{
return m_variant.template emplace<T>(std::forward<Args>(args)...); template <typename T, typename... Args>
} T& emplace(Args&&... args)
{
template <typename T, typename U, typename... Args> return m_variant.template emplace<T>(std::forward<Args>(args)...);
T& emplace(std::initializer_list<U> il, Args&&... args) }
{
return m_variant.template emplace<T>(il, std::forward<Args>(args)...); template <typename T, typename U, typename... Args>
} T& emplace(std::initializer_list<U> il, Args&&... args)
{
template <std::size_t I, typename... Args> return m_variant.template emplace<T>(il, std::forward<Args>(args)...);
std::variant_alternative_t<I, value_type>& emplace(Args&&... args) }
{
return m_variant.template emplace<I>(std::forward<Args>(args)...); template <std::size_t I, typename... Args>
} std::variant_alternative_t<I, value_type>& emplace(Args&&... args)
{
template <std::size_t I, typename U, typename... Args> return m_variant.template emplace<I>(std::forward<Args>(args)...);
std::variant_alternative_t<I, value_type>& emplace(std::initializer_list<U> il, Args&&... args) }
{
return m_variant.template emplace<I>(il, std::forward<Args>(args)...); template <std::size_t I, typename U, typename... Args>
} std::variant_alternative_t<I, value_type>& emplace(std::initializer_list<U> il, Args&&... args)
{
[[nodiscard]] constexpr std::size_t index() const noexcept return m_variant.template emplace<I>(il, std::forward<Args>(args)...);
{ }
return m_variant.index();
} [[nodiscard]] constexpr std::size_t index() const noexcept
{
[[nodiscard]] constexpr bool valueless_by_exception() const noexcept return m_variant.index();
{ }
return m_variant.valueless_by_exception();
} [[nodiscard]] constexpr bool valueless_by_exception() const noexcept
{
template <typename T> return m_variant.valueless_by_exception();
constexpr auto visit(T&& visitor) -> decltype(auto) }
{
return std::visit(std::forward<T>(visitor), m_variant); template <typename T>
} constexpr auto visit(T&& visitor) -> decltype(auto)
{
/** return std::visit(std::forward<T>(visitor), m_variant);
* Automatic visitor generation with empty default behavior }
* @param visitees user lambdas
*/ /**
template <typename... Visitee> * Automatic visitor generation
constexpr auto visit(Visitee&&... visitees) -> decltype(detail::member_func_detail<std::tuple<Visitee...>, std::tuple<Types...>>::make_return_type()) * @param visitees user lambdas
{ */
return std::visit(lambda_visitor{ template <typename... Visitee>
std::forward<Visitee>(visitees)..., constexpr auto visit(
[](auto) Visitee&&... visitees) -> typename detail::member_func_detail<std::tuple<Visitee...>, std::tuple<Types...>>::make_return_type
{ {
} return std::visit(lambda_visitor{std::forward<Visitee>(visitees)...}, m_variant);
}, m_variant); }
}
template <typename Default, typename... Visitee>
template <typename Default, typename... Visitee> constexpr auto visit_value(Default&& default_value, Visitee&&... visitees) -> decltype(auto)
constexpr auto visit_value(Default&& default_value, Visitee&&... visitees) -> decltype(auto) {
{ return std::visit(lambda_visitor{
return std::visit(lambda_visitor{ std::forward<Visitee>(visitees)...,
std::forward<Visitee>(visitees)..., [default_value=std::forward<Default>(default_value)](auto&& value) {
[default_value=std::forward<Default>(default_value)](auto&& value) return std::forward<decltype(value)>(value);
{ }
return std::forward<decltype(value)>(value); });
} }
});
} template <typename MemberFunc, typename... Args>
constexpr auto call_member(const MemberFunc func,
template <typename... MemberFuncs> Args&&... args) -> typename detail::member_func_detail<
constexpr auto call_member(const MemberFuncs... funcs) std::tuple<MemberFunc>, std::tuple<Types...>>::make_return_type
{ {
static_assert(std::conjunction_v<std::is_member_function_pointer<std::decay_t<MemberFuncs>>...>, return std::visit([func,...args=std::forward<Args>(args)](auto&& value) {
"Must provide only pointers to member functions!"); return ((value).*(func))(std::forward<Args>(args)...);
using meta_t = detail::member_func_detail<std::tuple<MemberFuncs...>, std::tuple<Types...>>; }, m_variant);
using result_t = decltype(meta_t::make_return_type()); }
return std::visit([=](auto&& value) -> result_t
{ template <size_t I>
using ValueType = std::decay_t<decltype(value)>; [[nodiscard]] constexpr bool has_index() const noexcept
{
if constexpr (std::disjunction_v<std::is_invocable<std::decay_t<decltype(funcs)>, ValueType>...>) return m_variant.index() == I;
{ }
}
template <typename T>
return *(... || ([&](auto&& func) -> decltype(auto) [[nodiscard]] constexpr bool has_type() const noexcept
{ {
using FuncType = std::decay_t<decltype(func)>; return std::holds_alternative<T>(m_variant);
using ReturnType = std::invoke_result_t<FuncType, ValueType>; }
if constexpr (std::is_invocable_v<FuncType, ValueType>)
{ template <typename T>
return ((value).*(func))(); [[nodiscard]] constexpr auto get() -> decltype(auto)
} {
return std::declval<result_t>(); return std::get<T>(m_variant);
}(cast_member_ptr<std::decay_t<decltype(value)>>(std::forward<decltype(funcs)>(funcs))))); }
}, m_variant);
} template <typename T>
[[nodiscard]] constexpr auto get() const -> decltype(auto)
template <typename MemberFunc, typename... Args> {
constexpr auto call_member_args(const MemberFunc func, Args&&... args) return std::get<T>(m_variant);
{ }
return std::visit([=](auto&& value)
{ template <size_t I>
return ((value).*(func))(std::forward<Args>(args)...); [[nodiscard]] constexpr auto get() -> decltype(auto)
}, m_variant); {
} return std::get<I>(m_variant);
}
template <size_t I>
[[nodiscard]] constexpr bool has_index() const noexcept template <size_t I>
{ [[nodiscard]] constexpr auto get() const -> decltype(auto)
return m_variant.index() == I; {
} return std::get<I>(m_variant);
}
template <typename T>
[[nodiscard]] constexpr bool has_type() const noexcept template <size_t I>
{ constexpr std::add_pointer_t<std::variant_alternative_t<I, value_type>> get_if() noexcept
return std::holds_alternative<T>(m_variant); {
} return std::get_if<I>(m_variant);
}
template <typename T>
[[nodiscard]] constexpr auto get() -> decltype(auto) template <size_t I>
{ constexpr std::add_pointer_t<const std::variant_alternative_t<I, value_type>> get_if() noexcept
return std::get<T>(m_variant); {
} return std::get_if<I>(m_variant);
}
template <typename T>
[[nodiscard]] constexpr auto get() const -> decltype(auto) template <typename T>
{ constexpr std::add_pointer_t<T> get_if() noexcept
return std::get<T>(m_variant); {
} return std::get_if<T>(m_variant);
}
template <size_t I>
[[nodiscard]] constexpr auto get() -> decltype(auto) template <typename T>
{ constexpr std::add_pointer_t<const T> get_if() noexcept
return std::get<I>(m_variant); {
} return std::get_if<T>(m_variant);
}
template <size_t I>
[[nodiscard]] constexpr auto get() const -> decltype(auto) template <typename T>
{ constexpr T value_or(T&& t) const
return std::get<I>(m_variant); {
} if (has_type<T>())
return get<T>();
template <size_t I> return std::forward<T>(t);
constexpr std::add_pointer_t<std::variant_alternative_t<I, value_type>> get_if() noexcept }
{
return std::get_if<I>(m_variant); template <size_t I>
} constexpr std::variant_alternative_t<I, value_type> value_or(const std::variant_alternative_t<I, value_type>& t) const
{
template <size_t I> if (has_type<std::variant_alternative_t<I, value_type>>())
constexpr std::add_pointer_t<const std::variant_alternative_t<I, value_type>> get_if() noexcept return get<I>();
{ return t;
return std::get_if<I>(m_variant); }
}
template <size_t I>
template <typename T> constexpr std::variant_alternative_t<I, value_type> value_or(std::variant_alternative_t<I, value_type>&& t) const
constexpr std::add_pointer_t<T> get_if() noexcept {
{ if (has_type<std::variant_alternative_t<I, value_type>>())
return std::get_if<T>(m_variant); return get<I>();
} return t;
}
template <typename T>
constexpr std::add_pointer_t<const T> get_if() noexcept template <size_t>
{ constexpr const value_type& variant() const
return std::get_if<T>(m_variant); {
} return m_variant;
}
template <typename T>
constexpr T value_or(T&& t) const constexpr value_type& variant()
{ {
if (has_type<T>()) return m_variant;
return get<T>(); }
return std::forward<T>(t);
} [[nodiscard]] constexpr size_t size() const
{
template <size_t I> return variant_size;
constexpr std::variant_alternative_t<I, value_type> value_or(const std::variant_alternative_t<I, value_type>& t) const }
{
if (has_type<std::variant_alternative_t<I, value_type>>()) friend bool operator==(const variant_t& lhs, const variant_t& rhs)
return get<I>(); {
return t; return lhs.m_variant == rhs.m_variant;
} }
template <size_t I> friend bool operator!=(const variant_t& lhs, const variant_t& rhs)
constexpr std::variant_alternative_t<I, value_type> value_or(std::variant_alternative_t<I, value_type>&& t) const {
{ return lhs.m_variant != rhs.m_variant;
if (has_type<std::variant_alternative_t<I, value_type>>()) }
return get<I>();
return t; friend bool operator<(const variant_t& lhs, const variant_t& rhs)
} {
return lhs.m_variant < rhs.m_variant;
template <size_t> }
constexpr const value_type& variant() const
{ friend bool operator>(const variant_t& lhs, const variant_t& rhs)
return m_variant; {
} return lhs.m_variant > rhs.m_variant;
}
constexpr value_type& variant()
{ friend bool operator<=(const variant_t& lhs, const variant_t& rhs)
return m_variant; {
} return lhs.m_variant <= rhs.m_variant;
}
[[nodiscard]] constexpr size_t size() const
{ friend bool operator>=(const variant_t& lhs, const variant_t& rhs)
return variant_size; {
} return lhs.m_variant >= rhs.m_variant;
}
friend bool operator==(const variant_t& lhs, const variant_t& rhs)
{ private:
return lhs.m_variant == rhs.m_variant; template <typename Derived, typename Base, typename ReturnType, typename... Args>
} static auto cast_member_ptr(ReturnType (Base::*base_func)(Args...))
{
friend bool operator!=(const variant_t& lhs, const variant_t& rhs) return reinterpret_cast<ReturnType (Derived::*)(Args...)>(base_func);
{ }
return lhs.m_variant != rhs.m_variant;
} value_type m_variant;
};
friend bool operator<(const variant_t& lhs, const variant_t& rhs)
{ namespace detail
return lhs.m_variant < rhs.m_variant; {
} template <typename>
class variant_is_base_of
friend bool operator>(const variant_t& lhs, const variant_t& rhs) {};
{
return lhs.m_variant > rhs.m_variant; template <typename... Types>
} class variant_is_base_of<variant_t<Types...>>
{
friend bool operator<=(const variant_t& lhs, const variant_t& rhs) public:
{ using value_type = bool;
return lhs.m_variant <= rhs.m_variant; template <typename T>
} static constexpr bool value = std::conjunction_v<std::is_base_of<T, Types>...>;
};
friend bool operator>=(const variant_t& lhs, const variant_t& rhs)
{ template <typename... Types>
return lhs.m_variant >= rhs.m_variant; class variant_is_base_of<std::variant<Types...>>
} {
public:
private: using value_type = bool;
template <typename Derived, typename Base, typename ReturnType, typename... Args> template <typename T>
static auto cast_member_ptr(ReturnType (Base::*base_func)(Args...)) static constexpr bool value = std::conjunction_v<std::is_base_of<T, Types>...>;
{ };
return reinterpret_cast<ReturnType (Derived::*)(Args...)>(base_func);
} template <typename T>
static constexpr bool variant_is_base_of_v = variant_is_base_of<T>::value;
value_type m_variant; }
};
namespace detail
{
template <typename>
class variant_is_base_of
{
};
template <typename... Types>
class variant_is_base_of<variant_t<Types...>>
{
public:
using value_type = bool;
template <typename T>
static constexpr bool value = std::conjunction_v<std::is_base_of<T, Types>...>;
};
template <typename... Types>
class variant_is_base_of<std::variant<Types...>>
{
public:
using value_type = bool;
template <typename T>
static constexpr bool value = std::conjunction_v<std::is_base_of<T, Types>...>;
};
template <typename T>
static constexpr bool variant_is_base_of_v = variant_is_base_of<T>::value;
}
} }
#endif //BLT_STD_VARIANT_H #endif //BLT_STD_VARIANT_H

@ -1 +1 @@
Subproject commit d88c5e15079047777b418132ece5879e7c9aaa2b Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3

135
tests/variant_tests.cpp Normal file
View File

@ -0,0 +1,135 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/std/variant.h>
#include <blt/logging/logging.h>
#include <blt/std/assert.h>
struct base_type
{
[[nodiscard]] virtual int simple() const = 0;
[[nodiscard]] virtual std::string to_string() const = 0;
virtual ~base_type() = default;
};
struct mutate_type : base_type
{
virtual void mutate(int i) = 0;
};
struct type1 final : base_type
{
[[nodiscard]] int simple() const override // NOLINT
{
return 1;
}
[[nodiscard]] std::string to_string() const override // NOLINT
{
return "Type1";
}
};
struct type2 final : base_type
{
[[nodiscard]] int simple() const override // NOLINT
{
return 2;
}
[[nodiscard]] std::string to_string() const override // NOLINT
{
return "Type2";
}
};
struct type3 final : base_type
{
[[nodiscard]] int simple() const override // NOLINT
{
return 3;
}
[[nodiscard]] std::string to_string() const override // NOLINT
{
return "Type3";
}
};
struct storing_type1 final : mutate_type
{
explicit storing_type1(const int i): internal(i)
{
}
[[nodiscard]] int simple() const override // NOLINT
{
return internal;
}
void mutate(const int i) override
{
internal = i;
}
[[nodiscard]] std::string to_string() const override // NOLINT
{
return "Storing Type: {" + std::to_string(internal) + "}";
}
int internal;
};
struct storing_type2 final : mutate_type
{
explicit storing_type2(const float i): internal(i * 2.2534f)
{
}
[[nodiscard]] int simple() const override // NOLINT
{
return static_cast<int>(internal);
}
void mutate(const int i) override
{
internal = static_cast<float>(i) * 2.2534f;
}
[[nodiscard]] std::string to_string() const override // NOLINT
{
return "Storing Type: {" + std::to_string(internal) + "}";
}
float internal;
};
int main()
{
blt::variant_t<type1, type2, type3> v1{type1{}};
blt::variant_t<type1, type2, type3> v2{type2{}};
blt::variant_t<type1, type2, type3> v3{type3{}};
BLT_TRACE("Variants to_string():");
BLT_TRACE("V1: {}", v1.call_member(&base_type::to_string));
BLT_TRACE("V2: {}", v2.call_member(&base_type::to_string));
BLT_TRACE("V3: {}", v3.call_member(&base_type::to_string));
}