diff --git a/CMakeLists.txt b/CMakeLists.txt index cfdc1ac..9ac8043 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 () diff --git a/include/blt/std/variant.h b/include/blt/std/variant.h index 0ea2c87..aa56eae 100644 --- a/include/blt/std/variant.h +++ b/include/blt/std/variant.h @@ -21,487 +21,457 @@ #include #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; - } + 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, typename filter_void::type, decltype(std::tuple_cat( + std::declval>(), std::declval::type>()))>; + }; + + template + using filter_void_t = typename filter_void::type; + + template + struct filter_invoke; + + template + struct filter_invoke + { + using type = std::tuple<>; + }; + + template + struct filter_invoke + { + using type = std::conditional_t, decltype(std::tuple_cat( + std::declval>(), std::declval::type>())), + typename filter_invoke::type>; + }; + + template + using filter_invoke_t = typename filter_invoke::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; + + bool has_value; + + explicit passthrough_value(const bool has_value): has_value(has_value) + {} + + explicit operator bool() const + { + return has_value; + } + }; + + 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{true}; + } + return passthrough_value{false}; + }()); + } + + 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 std::tuple_element_t<0, result_types>::return_type; + + template + using get_type = typename T::return_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>...>>; + } + + constexpr static bool all_has_void = std::decay_t( + std::index_sequence_for()))>>::value; + constexpr static bool all_has_ret = std::decay_t( + std::index_sequence_for()))>>::value; + constexpr static bool all_has_ret_or_void = std::decay_t( + std::index_sequence_for()))>>::value; + + using non_void_types = typename std::decay_t( + std::index_sequence_for()))>>::type; + + template + static constexpr auto make_variant(std::index_sequence) + { + using variant = variant_t>...>; + return std::declval(); + } + + using make_return_type = std::conditional_t, std::conditional_t< + std::tuple_size_v == 0, void, decltype(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 + * @param visitees user lambdas + */ + template + constexpr auto visit( + Visitee&&... visitees) -> typename detail::member_func_detail, std::tuple>::make_return_type + { + return std::visit(lambda_visitor{std::forward(visitees)...}, 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 MemberFunc func, + Args&&... args) -> typename detail::member_func_detail< + std::tuple, std::tuple>::make_return_type + { + return std::visit([func,...args=std::forward(args)](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 diff --git a/libraries/parallel-hashmap b/libraries/parallel-hashmap index d88c5e1..93201da 160000 --- a/libraries/parallel-hashmap +++ b/libraries/parallel-hashmap @@ -1 +1 @@ -Subproject commit d88c5e15079047777b418132ece5879e7c9aaa2b +Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3 diff --git a/tests/variant_tests.cpp b/tests/variant_tests.cpp new file mode 100644 index 0000000..9adecae --- /dev/null +++ b/tests/variant_tests.cpp @@ -0,0 +1,135 @@ +/* + * + * 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 . + */ +#include +#include +#include + +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(internal); + } + + void mutate(const int i) override + { + internal = static_cast(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 v1{type1{}}; + blt::variant_t v2{type2{}}; + blt::variant_t 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)); +} \ No newline at end of file