diff --git a/src/common/variant.h b/src/common/variant.h index a2121c75e..ffb34e40a 100644 --- a/src/common/variant.h +++ b/src/common/variant.h @@ -62,7 +62,6 @@ namespace tools //// // variant: convenience wrapper around boost::variant with a cleaner interface -// - the value may be assigned to but is otherwise read-only // - the variant is 'optional' - an empty variant will evaluate to 'false' and an initialized variant will be 'true' /// template @@ -70,6 +69,7 @@ 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(); } }; @@ -107,14 +107,20 @@ public: template bool is_type() const noexcept { return this->index() == this->type_index_of(); } - /// try to get a read-only handle to the embedded value (return nullptr on failure) + /// try to get a handle to the embedded value (return nullptr on failure) template - const T* try_unwrap() const - { - return boost::strict_get(&m_value); - } + 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 read-only handle to the embedded 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 { @@ -131,7 +137,7 @@ public: static constexpr int type_index_of() noexcept { using types = boost::mpl::vector; - using elem = typename boost::mpl::find::type; + using elem = typename boost::mpl::find::type; using begin = typename boost::mpl::begin::type; return boost::mpl::distance::value; } @@ -142,6 +148,11 @@ public: /// apply a visitor to the variant template + typename VisitorT::result_type visit(VisitorT &&visitor) + { + return boost::apply_visitor(std::forward(visitor), m_value); + } + template typename VisitorT::result_type visit(VisitorT &&visitor) const { return boost::apply_visitor(std::forward(visitor), m_value); diff --git a/tests/unit_tests/variant.cpp b/tests/unit_tests/variant.cpp index c22200a8c..d7ded8e4b 100644 --- a/tests/unit_tests/variant.cpp +++ b/tests/unit_tests/variant.cpp @@ -232,8 +232,10 @@ struct test_stringify_visitor: public variant_static_visitor return test_stringify_visitor::stringify(t); } }; -} // anonymous namespace //------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +} // anonymous namespace + //------------------------------------------------------------------------------------------------------------------- TEST(variant, operatorbool) { @@ -295,6 +297,25 @@ TEST(variant, unwrap) 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;