diff --git a/src/common/variant.h b/src/common/variant.h deleted file mode 100644 index b90833d2ef..0000000000 --- a/src/common/variant.h +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2022-2024, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Variant wrapper class. - -#pragma once - -//local headers - -//third party headers -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//standard headers -#include -#include -#include - -//forward declarations - - -namespace tools -{ -namespace detail -{ -template -struct value_initialize_on_which -{ - template - void operator()(T) { if (Variant::template type_index_of() == target_which) v = T(); } - - Variant &v; - const int target_which; -}; -} // namespace detail - -[[noreturn]] inline void variant_static_visitor_blank_err() -{ throw std::runtime_error("variant: tried to visit an empty variant."); } -[[noreturn]] inline void variant_unwrap_err() -{ throw std::runtime_error("variant: tried to access value of incorrect type."); } - -//// -// variant: convenience wrapper around boost::variant with a cleaner interface -// - the variant is 'optional' - an empty variant will evaluate to 'false' and an initialized variant will be 'true' -/// -template -struct variant_static_visitor : public boost::static_visitor -{ - /// provide visitation for empty variants - /// - add this to your visitor with: using variant_static_visitor::operator(); - [[noreturn]] ResultT operator()(const boost::blank) { variant_static_visitor_blank_err(); } - [[noreturn]] ResultT operator()(const boost::blank) const { variant_static_visitor_blank_err(); } -}; - -template -class variant -{ - using VType = boost::variant; - -public: -//constructors - /// default constructor - variant() = default; - - /// construct from variant type (use enable_if to avoid issues with copy/move constructor) - template >, - variant - >::value, - bool - >::type = true> - variant(T &&value) : m_value{std::forward(value)} {} - -//member functions - /// check the variant type - template - bool is_type() const noexcept { return this->index() == this->type_index_of(); } - - /// try to get a handle to the embedded value (return nullptr on failure) - template - T* try_unwrap() noexcept { return boost::strict_get< T>(&m_value); } - template - const T* try_unwrap() const noexcept { return boost::strict_get(&m_value); } - - /// get a handle to the embedded value - template - T& unwrap() - { - T *value_ptr{this->try_unwrap()}; - if (!value_ptr) variant_unwrap_err(); - return *value_ptr; - } - template - const T& unwrap() const - { - const T *value_ptr{this->try_unwrap()}; - if (!value_ptr) variant_unwrap_err(); - return *value_ptr; - } - - /// get the type index of the currently stored type - int index() const noexcept { return m_value.which(); } - - /// get the type index of a requested type (compile error for invalid types) (boost::mp11 is boost 1.66.0) - template - static constexpr int type_index_of() noexcept - { - using types = typename VType::types; - using elem = typename boost::mpl::find::type; - using begin = typename boost::mpl::begin::type; - return boost::mpl::distance::value; - } - - /// check if two variants have the same type - static bool same_type(const variant &v1, const variant &v2) noexcept - { return v1.index() == v2.index(); } - - /// apply a visitor to the variant - template - decltype(auto) visit(VisitorT &&visitor) // decltype(auto) since it forwards the return ref type correctly - { - return boost::apply_visitor(std::forward(visitor), m_value); - } - template - decltype(auto) visit(VisitorT &&visitor) const // decltype(auto) since it forwards the return ref type correctly - { - return boost::apply_visitor(std::forward(visitor), m_value); - } - - /// value initialize the variant based on a type index - void value_initialize_to_type_index(const int which) - { - if (which < 0 || which >= boost::mpl::size::type::value) - throw std::runtime_error("value_initialize_to_type_index: type index of out range"); - - detail::value_initialize_on_which viow{*this, which}; - boost::mpl::for_each(viow); - } - -private: -//member variables - /// variant of all value types - VType m_value; - -//friend functions -template -friend bool do_serialize(Archive &ar, variant &v); -}; - -template -class optional_variant: public variant -{ -public: -//constructors - /// default constructor - optional_variant() = default; - - /// construct from variant type (use enable_if to avoid issues with copy/move constructor) - template >, - optional_variant - >::value, - bool - >::type = true> - optional_variant(T &&value) : variant(std::forward(value)) {} - - // construct like boost::optional - optional_variant(boost::none_t) {} - -//overloaded operators - /// boolean operator: true if the variant isn't empty/uninitialized - explicit operator bool() const noexcept { return !this->is_empty(); } - -//member functions - /// check if empty/uninitialized - bool is_empty() const noexcept { return this->index() == 0; } -}; - -} //namespace tools diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 9674083dcb..0e4e1e7c80 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -43,7 +43,6 @@ #include #include #include -#include "common/variant.h" #include "serialization.h" /*! \struct variant_serialization_triats @@ -145,13 +144,3 @@ static bool do_serialize(Archive &ar, boost::variant &v) { return boost::apply_visitor(variant_write_visitor(ar), v); } - -// implementation for tools::variant delegates to internal boost::variant member field -namespace tools -{ -template -bool do_serialize(Archive &ar, variant &v) -{ - return do_serialize(ar, v.m_value); -} -} diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 6daf28efdc..a25ea073c9 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -90,7 +90,6 @@ set(unit_tests_sources hardfork.cpp unbound.cpp uri.cpp - variant.cpp util.cpp varint.cpp ver_rct_non_semantics_simple_cached.cpp diff --git a/tests/unit_tests/variant.cpp b/tests/unit_tests/variant.cpp deleted file mode 100644 index 634558a129..0000000000 --- a/tests/unit_tests/variant.cpp +++ /dev/null @@ -1,563 +0,0 @@ -// Copyright (c) 2023-2024, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "common/variant.h" - -#include -#include -#include - -#include "gtest/gtest.h" - -#include -#include -#include - -using tools::optional_variant; -using tools::variant; -using tools::variant_static_visitor; - -namespace -{ -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -using strip_all_t = std::remove_reference_t>; -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -using strip_same = std::is_same, strip_all_t>; -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -< - typename PositiveType, - typename TestType, - typename... VariantTypes, - class VecTypes = boost::mpl::vector, - class VecBegin = typename boost::mpl::begin::type, - class VecIndexT = typename boost::mpl::find::type, - size_t TYPE_INDEX = boost::mpl::distance::value, - bool LAST_VARIANT_TYPE = TYPE_INDEX == sizeof...(VariantTypes) - 1 -> -static std::enable_if_t -test_is_type_match(const variant& v) -{ - constexpr bool expected = strip_same(); - const bool actual = v.template is_type(); - EXPECT_EQ(expected, actual); - - EXPECT_FALSE(v.template is_type()); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -< - typename PositiveType, - typename TestType, - typename... VariantTypes, - class VecTypes = boost::mpl::vector, - class VecBegin = typename boost::mpl::begin::type, - class VecIndexT = typename boost::mpl::find::type, - size_t TYPE_INDEX = boost::mpl::distance::value, - bool LAST_VARIANT_TYPE = TYPE_INDEX == sizeof...(VariantTypes) - 1 -> -static std::enable_if_t -test_is_type_match(const variant& v) -{ - constexpr bool expected = strip_same(); - const bool actual = v.template is_type(); - EXPECT_EQ(expected, actual); - - using NextTypeIt = typename boost::mpl::advance>::type; - using NextTestType = typename boost::mpl::deref::type; - test_is_type_match(v); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -< - typename VariantType0, - typename... VariantTypesRest, - typename AssignType -> -static void test_is_type_ref -( - variant& v, - AssignType&& val -) -{ - v = val; - test_is_type_match(v); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -< - typename VariantType0, - typename... VariantTypesRest, - typename AssignType0, - typename... AssignTypesRest -> -static void test_is_type_ref -( - variant& v, - AssignType0&& val_0, - AssignTypesRest&&... val_rest -) -{ - v = val_0; - test_is_type_match(v); - test_is_type_ref(v, val_rest...); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -static void test_is_type_full(VariantTypes&&... test_vals) -{ - variant v; - test_is_type_ref(v, test_vals...); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -< - size_t IJ = 0, - typename... VariantTypes, - bool END = IJ == sizeof...(VariantTypes) * sizeof...(VariantTypes) -> -static std::enable_if_t -test_same_type_ref -( - variant& v1, - variant& v2, - const std::tuple& tup_i, - const std::tuple& tup_j -) -{ /* trivial end case */ } -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -< - size_t IJ = 0, - typename... VariantTypes, - bool END = IJ == sizeof...(VariantTypes) * sizeof...(VariantTypes) -> -static std::enable_if_t -test_same_type_ref -( - variant& v1, - variant& v2, - const std::tuple& tup_i, - const std::tuple& tup_j -) -{ - constexpr size_t I = IJ / sizeof...(VariantTypes); - constexpr size_t J = IJ % sizeof...(VariantTypes); - constexpr bool expected = I == J; - - v1 = std::get(tup_i); - v2 = std::get(tup_j); - const bool actual = variant::same_type(v1, v2); - - EXPECT_EQ(expected, actual); - - test_same_type_ref(v1, v2, tup_i, tup_j); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -template -static void test_same_type_full -( - const std::tuple& vals_i, - const std::tuple& vals_j -) -{ - using Variant = variant; - Variant v_i; - Variant v_j; - test_same_type_ref(v_i, v_j, vals_i, vals_j); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -struct test_stringify_visitor: public variant_static_visitor -{ - template - static std::string stringify(const T& t) - { - std::stringstream ss; - ss << typeid(T).name(); - ss << "::"; - ss << t; - return ss.str(); - } - - template - static void test_visitation(const Variant& v, const T& t) - { - EXPECT_EQ(test_stringify_visitor::stringify(t), v.visit(test_stringify_visitor())); - } - - // Make sure boost::blank errors - using variant_static_visitor::operator(); - - // Visitation implementation - template - std::string operator()(const T& t) const - { - return test_stringify_visitor::stringify(t); - } -}; -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- -} // anonymous namespace - -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, operatorbool) -{ - optional_variant v; - EXPECT_FALSE(v); - v = (int16_t) 2023; - EXPECT_TRUE(v); - v = (int16_t) 0; - EXPECT_TRUE(v); - v = boost::blank{}; - EXPECT_FALSE(v); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, is_empty) -{ - optional_variant v; - EXPECT_TRUE(v.is_empty()); - v = (int16_t) 2023; - EXPECT_FALSE(v.is_empty()); - v = (int16_t) 0; - EXPECT_FALSE(v.is_empty()); - v = boost::blank{}; - EXPECT_TRUE(v.is_empty()); - - optional_variant<> v2; - EXPECT_TRUE(v2.is_empty()); - v2 = boost::blank{}; - EXPECT_TRUE(v2.is_empty()); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, is_type) -{ - variant v; - EXPECT_TRUE(v.is_type()); - v = (int16_t) 2023; - EXPECT_TRUE(v.is_type()); - - test_is_type_full((uint32_t) 2023, (char) '\n', std::string("HOWDY")); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, try_unwrap) -{ - variant v; - EXPECT_TRUE(v.try_unwrap()); - v = (int16_t) 5252; - ASSERT_TRUE(v.try_unwrap()); - EXPECT_EQ(5252, *v.try_unwrap()); - EXPECT_FALSE(v.try_unwrap()); - EXPECT_FALSE(v.try_unwrap()); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, unwrap) -{ - variant v; - EXPECT_EQ(0, v.unwrap()); - v = (int16_t) 5252; - EXPECT_EQ(5252, v.unwrap()); - EXPECT_THROW(v.unwrap(), std::runtime_error); - EXPECT_THROW(v.unwrap(), std::runtime_error); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, mutation) -{ - variant v; - v = (uint8_t) 5; - EXPECT_EQ(5, v.unwrap()); - uint8_t &intref{v.unwrap()}; - intref = 10; - EXPECT_EQ(10, v.unwrap()); - EXPECT_TRUE(v.try_unwrap()); - uint8_t *intptr{v.try_unwrap()}; - *intptr = 15; - EXPECT_EQ(15, v.unwrap()); - - const variant &v_ref{v}; - EXPECT_EQ(15, v_ref.unwrap()); - EXPECT_TRUE(v_ref.try_unwrap()); - EXPECT_EQ(15, *(v_ref.try_unwrap())); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, index) -{ - variant v; - EXPECT_EQ(0, v.index()); - v = (int8_t) 7; - EXPECT_EQ(0, v.index()); - v = (uint8_t) 7; - EXPECT_EQ(1, v.index()); - v = (int16_t) 7; - EXPECT_EQ(2, v.index()); - v = (uint16_t) 7; - EXPECT_EQ(3, v.index()); - v = "verifiable variant vying for vengence versus visa"; - EXPECT_EQ(4, v.index()); - - optional_variant vo; - EXPECT_EQ(0, vo.index()); - vo = (int8_t) 7; - EXPECT_EQ(1, vo.index()); - vo = (uint8_t) 7; - EXPECT_EQ(2, vo.index()); - vo = (int16_t) 7; - EXPECT_EQ(3, vo.index()); - vo = (uint16_t) 7; - EXPECT_EQ(4, vo.index()); - vo = "verifiable variant vying for vengence versus visa"; - EXPECT_EQ(5, vo.index()); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, type_index_of) -{ - variant v; - EXPECT_EQ(0, decltype(v)::type_index_of()); - EXPECT_EQ(1, decltype(v)::type_index_of()); - EXPECT_EQ(2, decltype(v)::type_index_of()); - EXPECT_EQ(3, decltype(v)::type_index_of()); - EXPECT_EQ(4, decltype(v)::type_index_of()); - - optional_variant vo; - EXPECT_EQ(0, decltype(vo)::type_index_of()); - EXPECT_EQ(1, decltype(vo)::type_index_of()); - EXPECT_EQ(2, decltype(vo)::type_index_of()); - EXPECT_EQ(3, decltype(vo)::type_index_of()); - EXPECT_EQ(4, decltype(vo)::type_index_of()); - EXPECT_EQ(5, decltype(vo)::type_index_of()); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, constexpr_type_index_of) -{ - variant v; - constexpr int TINDEX2 = decltype(v)::type_index_of(); - EXPECT_EQ(2, TINDEX2); - constexpr int TINDEX4 = decltype(v)::type_index_of(); - EXPECT_EQ(4, TINDEX4); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, same_type) -{ - const std::tuple vals_i(77840, "Hullubaloo", '\0'); - const std::tuple vals_j(1876, "Canneck", '\t'); - test_same_type_full(vals_i, vals_j); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, visit) -{ - variant v; - - v = "Rev"; - test_stringify_visitor::test_visitation(v, std::string("Rev")); - - v = (int16_t) 2001; - test_stringify_visitor::test_visitation(v, (int16_t) 2001); - EXPECT_NE(test_stringify_visitor::stringify((uint16_t) 2001), v.visit(test_stringify_visitor())); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, visit_lambda) -{ - const auto stringify_lambda = [](auto x) -> std::string - { - if constexpr (std::is_same_v) - return x; - else if constexpr (std::is_same_v) - throw std::runtime_error("boost blank cannot be stringified"); - else - return std::to_string(x); - }; - - optional_variant v; - EXPECT_THROW(v.visit(stringify_lambda), std::runtime_error); - - v = "Rev"; - EXPECT_EQ("Rev", v.visit(stringify_lambda)); - - v = (int16_t) 2001; - EXPECT_EQ("2001", v.visit(stringify_lambda)); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, visit_ref_passthru) -{ - struct A - { - int x; - }; - - struct B - { - int x; - }; - - struct x_ref_visitor: tools::variant_static_visitor - { - using tools::variant_static_visitor::operator(); - - const int& operator()(const A &a) const { return a.x; } - const int& operator()(const B &b) const { return b.x; } - }; - - optional_variant v; - EXPECT_THROW(v.visit(x_ref_visitor{}), std::runtime_error); - - // A very hairy looking test, but we're just testing that the reference returned from our static - // visitor is actually pointing to something in the same stack space as our variant operand. - // This will let us catch mistakes where we take a reference to a locally created variable if - // the visit() method is changed subtlely. - v = A { 2024 }; - const char * const px = reinterpret_cast(std::addressof(v.visit(x_ref_visitor{}))); - const char * const pv = reinterpret_cast(&v); - EXPECT_LT(px - pv, sizeof(v)); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, value_initialize_to_type_index) -{ - optional_variant v; - for (int i = 0; i < 6; ++i) - { - v.value_initialize_to_type_index(i); - EXPECT_EQ(i, v.index()); - } - - v = (int8_t) 69; - EXPECT_EQ(1, v.index()); - EXPECT_EQ(69, v.unwrap()); - v.value_initialize_to_type_index(1); - EXPECT_EQ(1, v.index()); - EXPECT_EQ(0, v.unwrap()); - - v = (uint8_t) 69; - EXPECT_EQ(2, v.index()); - EXPECT_EQ(69, v.unwrap()); - v.value_initialize_to_type_index(2); - EXPECT_EQ(2, v.index()); - EXPECT_EQ(0, v.unwrap()); - - v = (int16_t) 69; - EXPECT_EQ(3, v.index()); - EXPECT_EQ(69, v.unwrap()); - v.value_initialize_to_type_index(3); - EXPECT_EQ(3, v.index()); - EXPECT_EQ(0, v.unwrap()); - - v = (uint16_t) 69; - EXPECT_EQ(4, v.index()); - EXPECT_EQ(69, v.unwrap()); - v.value_initialize_to_type_index(4); - EXPECT_EQ(4, v.index()); - EXPECT_EQ(0, v.unwrap()); - - v = std::string("69"); - EXPECT_EQ(5, v.index()); - EXPECT_EQ("69", v.unwrap()); - v.value_initialize_to_type_index(5); - EXPECT_EQ(5, v.index()); - EXPECT_EQ("", v.unwrap()); - - v = (int16_t) 69; - v.value_initialize_to_type_index(5); - EXPECT_EQ("", v.unwrap()); - - EXPECT_THROW(v.value_initialize_to_type_index(-1), std::runtime_error); - EXPECT_THROW(v.value_initialize_to_type_index(6), std::runtime_error); -} -//------------------------------------------------------------------------------------------------------------------- -TEST(variant, ad_hoc_recursion) -{ - struct left_t; - struct right_t; - - using twisty = optional_variant, boost::recursive_wrapper>; - - struct left_t - { - twisty l; - }; - - struct right_t - { - twisty r; - }; - - auto right = [](twisty&& t = {}) -> twisty - { - right_t r; - r.r = t; - return r; - }; - - auto left = [](twisty&& t = {}) -> twisty - { - left_t l; - l.l = t; - return l; - }; - - struct twisty_counter: variant_static_visitor> - { - std::pair operator()(boost::blank) const - { - return {0, 0}; - } - - std::pair operator()(const left_t& l) const - { - auto count = l.l.visit(twisty_counter()); - count.first += 1; - return count; - } - - std::pair operator()(const right_t& r) const - { - auto count = r.r.visit(twisty_counter()); - count.second += 1; - return count; - } - }; - - const twisty tw = left(left(right(right(left(right(left(right(left())))))))); - - int left_count, right_count; - std::tie(left_count, right_count) = tw.visit(twisty_counter()); - - EXPECT_EQ(5, left_count); - EXPECT_EQ(4, right_count); -} -//-------------------------------------------------------------------------------------------------------------------