variant: add mutable unwrap and visitation

This commit is contained in:
koe 2023-02-12 13:00:53 -06:00
parent bc3cec4634
commit 2a7435e026
2 changed files with 41 additions and 9 deletions

View file

@ -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 <typename ResultT>
@ -70,6 +69,7 @@ struct variant_static_visitor : public boost::static_visitor<ResultT>
{
/// 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 <typename T>
bool is_type() const noexcept { return this->index() == this->type_index_of<T>(); }
/// 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 <typename T>
const T* try_unwrap() const
{
return boost::strict_get<T>(&m_value);
}
T* try_unwrap() noexcept { return boost::strict_get< T>(&m_value); }
template <typename T>
const T* try_unwrap() const noexcept { return boost::strict_get<const T>(&m_value); }
/// get a read-only handle to the embedded value
/// get a handle to the embedded value
template <typename T>
T& unwrap()
{
T *value_ptr{this->try_unwrap<T>()};
if (!value_ptr) variant_unwrap_err();
return *value_ptr;
}
template <typename T>
const T& unwrap() const
{
@ -131,7 +137,7 @@ public:
static constexpr int type_index_of() noexcept
{
using types = boost::mpl::vector<boost::blank, Types...>;
using elem = typename boost::mpl::find<types, T>::type;
using elem = typename boost::mpl::find<types, T>::type;
using begin = typename boost::mpl::begin<types>::type;
return boost::mpl::distance<begin, elem>::value;
}
@ -142,6 +148,11 @@ public:
/// apply a visitor to the variant
template <typename VisitorT>
typename VisitorT::result_type visit(VisitorT &&visitor)
{
return boost::apply_visitor(std::forward<VisitorT>(visitor), m_value);
}
template <typename VisitorT>
typename VisitorT::result_type visit(VisitorT &&visitor) const
{
return boost::apply_visitor(std::forward<VisitorT>(visitor), m_value);

View file

@ -232,8 +232,10 @@ struct test_stringify_visitor: public variant_static_visitor<std::string>
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::string>(), std::runtime_error);
}
//-------------------------------------------------------------------------------------------------------------------
TEST(variant, mutation)
{
variant<uint8_t> v;
v = (uint8_t) 5;
EXPECT_EQ(5, v.unwrap<uint8_t>());
uint8_t &intref{v.unwrap<uint8_t>()};
intref = 10;
EXPECT_EQ(10, v.unwrap<uint8_t>());
EXPECT_TRUE(v.try_unwrap<uint8_t>());
uint8_t *intptr{v.try_unwrap<uint8_t>()};
*intptr = 15;
EXPECT_EQ(15, v.unwrap<uint8_t>());
const variant<uint8_t> &v_ref{v};
EXPECT_EQ(15, v_ref.unwrap<uint8_t>());
EXPECT_TRUE(v_ref.try_unwrap<uint8_t>());
EXPECT_EQ(15, *(v_ref.try_unwrap<uint8_t>()));
}
//-------------------------------------------------------------------------------------------------------------------
TEST(variant, index)
{
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;