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