partial variant
parent
f0e9475dcc
commit
8f3cbcb2fd
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
include(cmake/color.cmake)
|
||||
set(BLT_VERSION 5.4.5)
|
||||
set(BLT_VERSION 5.4.6)
|
||||
|
||||
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_argparse tests/argparse_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")
|
||||
endif ()
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <blt/std/types.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace blt
|
||||
{
|
||||
|
@ -45,19 +45,33 @@ namespace blt
|
|||
template <typename T, typename... Ts>
|
||||
struct filter_void<T, Ts...>
|
||||
{
|
||||
using type = std::conditional_t<
|
||||
std::is_same_v<T, void>,
|
||||
typename filter_void<Ts...>::type,
|
||||
decltype(std::tuple_cat(
|
||||
std::declval<std::tuple<T>>(),
|
||||
std::declval<typename filter_void<Ts...>::type>()
|
||||
))
|
||||
>;
|
||||
using type = std::conditional_t<std::is_same_v<T, void>, typename filter_void<Ts...>::type, decltype(std::tuple_cat(
|
||||
std::declval<std::tuple<T>>(), std::declval<typename filter_void<Ts...>::type>()))>;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
using filter_void_t = typename filter_void<Ts...>::type;
|
||||
|
||||
template <typename Type, typename... Ts>
|
||||
struct filter_invoke;
|
||||
|
||||
template <typename Type>
|
||||
struct filter_invoke<Type>
|
||||
{
|
||||
using type = std::tuple<>;
|
||||
};
|
||||
|
||||
template <typename Type, typename T, typename... Ts>
|
||||
struct filter_invoke<Type, T, Ts...>
|
||||
{
|
||||
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>())),
|
||||
typename filter_invoke<Ts...>::type>;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
using filter_invoke_t = typename filter_invoke<Ts...>::type;
|
||||
|
||||
template <typename Type, typename Func>
|
||||
struct member_func_meta
|
||||
{
|
||||
|
@ -66,16 +80,21 @@ namespace blt
|
|||
using return_type = std::conditional_t<can_invoke::value, std::invoke_result_t<Func, Type>, void>;
|
||||
};
|
||||
|
||||
template <typename T, typename Func, size_t Index, bool HasValue>
|
||||
template <typename T, typename Func, size_t Index>
|
||||
struct passthrough_value
|
||||
{
|
||||
using type = T;
|
||||
using func = Func;
|
||||
constexpr static size_t index = Index;
|
||||
|
||||
bool has_value;
|
||||
|
||||
explicit passthrough_value(const bool has_value): has_value(has_value)
|
||||
{}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return HasValue;
|
||||
return has_value;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -85,14 +104,13 @@ namespace blt
|
|||
template <size_t... Indexes>
|
||||
constexpr static auto find_func(std::index_sequence<Indexes...>)
|
||||
{
|
||||
return (... || []()
|
||||
{
|
||||
return (... || []() {
|
||||
using Meta = member_func_meta<Type, Funcs>;
|
||||
if constexpr (Meta::can_invoke::value)
|
||||
{
|
||||
return passthrough_value<typename Meta::return_type, Funcs, Indexes, true>{};
|
||||
return passthrough_value<typename Meta::return_type, Funcs, Indexes>{true};
|
||||
}
|
||||
return passthrough_value<void, Funcs, Indexes, false>{};
|
||||
return passthrough_value<typename Meta::return_type, Funcs, Indexes>{false};
|
||||
}());
|
||||
}
|
||||
|
||||
|
@ -103,56 +121,51 @@ namespace blt
|
|||
};
|
||||
|
||||
template <typename FuncTuple, typename ArgTuple>
|
||||
struct member_func_detail
|
||||
{
|
||||
};
|
||||
struct member_func_detail;
|
||||
|
||||
template <typename... Funcs, typename... Args>
|
||||
struct member_func_detail<std::tuple<Funcs...>, std::tuple<Args...>>
|
||||
{
|
||||
using result_types = std::tuple<first_invoke_member_func<Args, Funcs...>...>;
|
||||
using base_type = typename decltype(std::get<0>(std::declval<result_types>))::type;
|
||||
using base_type = typename std::tuple_element_t<0, result_types>::return_type;
|
||||
|
||||
template <typename T>
|
||||
using get_type = typename T::type;
|
||||
using get_type = typename T::return_type;
|
||||
|
||||
template <typename T>
|
||||
using is_base = std::is_same<typename T::type, base_type>;
|
||||
using is_base = std::is_same<T, base_type>;
|
||||
|
||||
template <typename T>
|
||||
using is_base_or_void = std::disjunction<std::is_void<typename T::type>, is_base<typename T::type>>;
|
||||
using is_base_or_void = std::disjunction<std::is_void<typename T::return_type>, is_base<typename T::return_type>>;
|
||||
|
||||
template <template<typename...> typename Functor, template<typename> typename PerType, size_t... Indexes>
|
||||
constexpr static auto for_each_type(std::index_sequence<Indexes...>)
|
||||
{
|
||||
return std::declval<Functor<PerType<decltype(std::get<Indexes>(std::declval<result_types>()))>...>>;
|
||||
return std::declval<Functor<PerType<std::tuple_element_t<Indexes, result_types>>...>>;
|
||||
}
|
||||
|
||||
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...>());
|
||||
constexpr static bool all_has_ret_or_void = for_each_type<std::conjunction_v, is_base_or_void>(std::index_sequence_for<Args...>());
|
||||
constexpr static bool all_has_void = std::decay_t<std::invoke_result_t<decltype(for_each_type<std::conjunction, std::is_void>(
|
||||
std::index_sequence_for<Args...>()))>>::value;
|
||||
constexpr static bool all_has_ret = std::decay_t<std::invoke_result_t<decltype(for_each_type<std::conjunction, is_base>(
|
||||
std::index_sequence_for<Args...>()))>>::value;
|
||||
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>(
|
||||
std::index_sequence_for<Args...>()))>>::value;
|
||||
|
||||
using non_void_types = typename decltype(for_each_type<filter_void_t, get_type>(std::index_sequence_for<Args...>()))::type;
|
||||
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...>)
|
||||
{
|
||||
if constexpr (all_has_void)
|
||||
return;
|
||||
using variant = variant_t<decltype(std::get<Indexes>(std::declval<non_void_types>()))...>;
|
||||
using variant = variant_t<std::decay_t<std::tuple_element_t<Indexes, non_void_types>>...>;
|
||||
return std::declval<variant>();
|
||||
}
|
||||
|
||||
static constexpr auto make_return_type()
|
||||
{
|
||||
if constexpr (all_has_void)
|
||||
return;
|
||||
if constexpr (all_has_ret)
|
||||
return std::declval<base_type>();
|
||||
if constexpr (all_has_ret_or_void)
|
||||
return std::declval<std::optional<base_type>>();
|
||||
return make_variant(std::make_index_sequence<std::tuple_size_v<non_void_types>>{});
|
||||
}
|
||||
using make_return_type = std::conditional_t<all_has_void, void, std::conditional_t<
|
||||
all_has_ret, base_type, std::conditional_t<
|
||||
all_has_ret_or_void, std::optional<base_type>, std::conditional_t<
|
||||
std::tuple_size_v<non_void_types> == 0, void, decltype(make_variant(
|
||||
std::make_index_sequence<std::tuple_size_v<non_void_types>>{}))>>>>;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -168,13 +181,13 @@ namespace blt
|
|||
using TLambdas::operator()...;
|
||||
};
|
||||
|
||||
#if __cplusplus < 202002L
|
||||
#if __cplusplus < 202002L
|
||||
|
||||
// explicit deduction guide (not needed as of C++20)
|
||||
template <typename... TLambdas>
|
||||
lambda_visitor(TLambdas...) -> lambda_visitor<TLambdas...>;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template <typename... Types>
|
||||
class variant_t
|
||||
|
@ -184,51 +197,41 @@ namespace blt
|
|||
size_t variant_size = sizeof...(Types);
|
||||
|
||||
constexpr variant_t(): m_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(const value_type& variant) noexcept(std::is_nothrow_copy_constructible_v<value_type>): m_variant(variant)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
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(Types&&... args) noexcept(std::is_nothrow_constructible_v<value_type, Types...>): m_variant(
|
||||
std::forward<Types>(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 <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 <size_t I, 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)...)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
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(
|
||||
std::in_place_index<I>, il, std::forward<C_Args>(args)...)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T& emplace(Args&&... args)
|
||||
|
@ -271,18 +274,14 @@ namespace blt
|
|||
}
|
||||
|
||||
/**
|
||||
* Automatic visitor generation with empty default behavior
|
||||
* Automatic visitor generation
|
||||
* @param visitees user lambdas
|
||||
*/
|
||||
template <typename... Visitee>
|
||||
constexpr auto visit(Visitee&&... visitees) -> decltype(detail::member_func_detail<std::tuple<Visitee...>, std::tuple<Types...>>::make_return_type())
|
||||
constexpr auto visit(
|
||||
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)...,
|
||||
[](auto)
|
||||
{
|
||||
}
|
||||
}, m_variant);
|
||||
return std::visit(lambda_visitor{std::forward<Visitee>(visitees)...}, m_variant);
|
||||
}
|
||||
|
||||
template <typename Default, typename... Visitee>
|
||||
|
@ -290,46 +289,18 @@ namespace blt
|
|||
{
|
||||
return std::visit(lambda_visitor{
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename... MemberFuncs>
|
||||
constexpr auto call_member(const MemberFuncs... funcs)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_member_function_pointer<std::decay_t<MemberFuncs>>...>,
|
||||
"Must provide only pointers to member functions!");
|
||||
using meta_t = detail::member_func_detail<std::tuple<MemberFuncs...>, std::tuple<Types...>>;
|
||||
using result_t = decltype(meta_t::make_return_type());
|
||||
return std::visit([=](auto&& value) -> result_t
|
||||
{
|
||||
using ValueType = std::decay_t<decltype(value)>;
|
||||
|
||||
if constexpr (std::disjunction_v<std::is_invocable<std::decay_t<decltype(funcs)>, ValueType>...>)
|
||||
{
|
||||
}
|
||||
|
||||
return *(... || ([&](auto&& func) -> decltype(auto)
|
||||
{
|
||||
using FuncType = std::decay_t<decltype(func)>;
|
||||
using ReturnType = std::invoke_result_t<FuncType, ValueType>;
|
||||
if constexpr (std::is_invocable_v<FuncType, ValueType>)
|
||||
{
|
||||
return ((value).*(func))();
|
||||
}
|
||||
return std::declval<result_t>();
|
||||
}(cast_member_ptr<std::decay_t<decltype(value)>>(std::forward<decltype(funcs)>(funcs)))));
|
||||
}, m_variant);
|
||||
}
|
||||
|
||||
template <typename MemberFunc, typename... Args>
|
||||
constexpr auto call_member_args(const MemberFunc func, Args&&... args)
|
||||
{
|
||||
return std::visit([=](auto&& value)
|
||||
constexpr auto call_member(const MemberFunc func,
|
||||
Args&&... args) -> typename detail::member_func_detail<
|
||||
std::tuple<MemberFunc>, std::tuple<Types...>>::make_return_type
|
||||
{
|
||||
return std::visit([func,...args=std::forward<Args>(args)](auto&& value) {
|
||||
return ((value).*(func))(std::forward<Args>(args)...);
|
||||
}, m_variant);
|
||||
}
|
||||
|
@ -478,8 +449,7 @@ namespace blt
|
|||
{
|
||||
template <typename>
|
||||
class variant_is_base_of
|
||||
{
|
||||
};
|
||||
{};
|
||||
|
||||
template <typename... Types>
|
||||
class variant_is_base_of<variant_t<Types...>>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d88c5e15079047777b418132ece5879e7c9aaa2b
|
||||
Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3
|
|
@ -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));
|
||||
}
|
Loading…
Reference in New Issue