implement visit pattern

This commit is contained in:
Francisco Paisana 2020-03-29 18:28:34 +01:00 committed by Francisco Paisana
parent 278a1686fb
commit 018f9e7db2
2 changed files with 120 additions and 34 deletions

View File

@ -283,7 +283,7 @@ public:
using base_t::get_if; using base_t::get_if;
using base_t::is; using base_t::is;
choice_t() noexcept { this_union().construct_unsafe(); } choice_t() noexcept { base_t::construct_unsafe(); }
choice_t(const choice_t<Args...>& other) noexcept { base_t::copy_unsafe(other); } choice_t(const choice_t<Args...>& other) noexcept { base_t::copy_unsafe(other); }
@ -325,20 +325,38 @@ public:
choice_t& operator=(choice_t&& other) noexcept choice_t& operator=(choice_t&& other) noexcept
{ {
base_t::dtor_unsafe(); base_t::dtor_unsafe();
base_t::move_unsafe(other); base_t::move_unsafe(std::move(other));
return *this; return *this;
} }
bool holds_same_type(const choice_t& other) noexcept { return base_t::type_id == other.type_id; }
template <typename Functor>
void visit(Functor&& f)
{
choice_details::visit<Functor, Args...>(*static_cast<base_t*>(this), f);
}
template <typename Functor>
void visit(Functor&& f) const
{
choice_details::visit<Functor, Args...>(*static_cast<const base_t*>(this), f);
}
private: private:
base_t& this_union() { return *this; }
const base_t& this_union() const { return *this; }
}; };
// template <typename Functor, typename First, typename... Args> template <typename Functor, typename... Args>
// void visit(choice_t<First, Args...>& u, Functor&& f) void visit(choice_t<Args...>& u, Functor&& f)
//{ {
// choice_details::visit_impl<Functor, decltype(u), First, Args...>::template apply(u, f); u.visit(std::forward<Functor>(f));
//} }
template <typename Functor, typename... Args>
void visit(const choice_t<Args...>& u, Functor&& f)
{
u.visit(std::forward<Functor>(f));
}
} // namespace srslte } // namespace srslte

View File

@ -22,26 +22,6 @@
#include "srslte/common/choice_type.h" #include "srslte/common/choice_type.h"
#include "srslte/common/test_common.h" #include "srslte/common/test_common.h"
namespace srslte {
namespace choice_details {
static_assert(static_max<1, 2>::value == 2, "StaticMax not working");
static_assert(static_max<2, 1>::value == 2, "StaticMax not working");
static_assert(type_indexer<double, char, float, int, long, double>::index == 0, "Type indexer not working");
static_assert(type_indexer<double, double, float, int, long, char>::index == 4, "Type indexer not working");
static_assert(type_indexer<double, char, float, int>::index == invalid_idx, "Type Indexer not working");
static_assert(sizeof(choice_storage_t<5, 4>) == 8, "Size of storage wrongly computed");
static_assert(alignof(choice_storage_t<5, 4>) == 4, "Alignment of storage wrongly computed");
static_assert(std::is_same<typename type_get<0, char, float, int, long, double>::type, double>::value,
"type index-based search not working");
static_assert(std::is_same<typename type_get<3, char, float, int, long, double>::type, float>::value,
"type index-based search not working");
static_assert(std::is_same<tagged_union_t<char, int, double>::default_type, char>::value,
"Default type is incorrect\n");
static_assert(tagged_union_t<char, int, double>::can_hold_type<int>(), "Can hold type implementation is incorrect\n");
static_assert(not tagged_union_t<char, int, double>::can_hold_type<uint8_t>(),
"Can hold type implementation is incorrect\n");
struct C { struct C {
static int counter; static int counter;
C() { counter++; } C() { counter++; }
@ -78,8 +58,32 @@ struct D {
}; };
int D::counter = 0; int D::counter = 0;
namespace srslte {
namespace choice_details {
static_assert(static_max<1, 2>::value == 2, "StaticMax not working");
static_assert(static_max<2, 1>::value == 2, "StaticMax not working");
static_assert(type_indexer<double, char, float, int, long, double>::index == 0, "Type indexer not working");
static_assert(type_indexer<double, double, float, int, long, char>::index == 4, "Type indexer not working");
static_assert(type_indexer<double, char, float, int>::index == invalid_idx, "Type Indexer not working");
static_assert(sizeof(choice_storage_t<5, 4>) == 8, "Size of storage wrongly computed");
static_assert(alignof(choice_storage_t<5, 4>) == 4, "Alignment of storage wrongly computed");
static_assert(std::is_same<typename type_get<0, char, float, int, long, double>::type, double>::value,
"type index-based search not working");
static_assert(std::is_same<typename type_get<3, char, float, int, long, double>::type, float>::value,
"type index-based search not working");
static_assert(std::is_same<tagged_union_t<char, int, double>::default_type, char>::value,
"Default type is incorrect\n");
static_assert(tagged_union_t<char, int, double>::can_hold_type<int>(), "Can hold type implementation is incorrect\n");
static_assert(not tagged_union_t<char, int, double>::can_hold_type<uint8_t>(),
"Can hold type implementation is incorrect\n");
} // namespace choice_details
} // namespace srslte
int test_tagged_union() int test_tagged_union()
{ {
using srslte::choice_details::tagged_union_t;
tagged_union_t<char, int, double, C> u; tagged_union_t<char, int, double, C> u;
u.construct_unsafe(5); u.construct_unsafe(5);
TESTASSERT(u.is<int>()); TESTASSERT(u.is<int>());
@ -98,6 +102,9 @@ int test_tagged_union()
int test_choice() int test_choice()
{ {
using srslte::choice_t;
using srslte::choice_details::bad_choice_access;
TESTASSERT(C::counter == 0); TESTASSERT(C::counter == 0);
TESTASSERT(D::counter == 0); TESTASSERT(D::counter == 0);
{ {
@ -118,7 +125,7 @@ int test_choice()
char n = '1'; char n = '1';
n = c2.get<char>(); n = c2.get<char>();
TESTASSERT(n == '1'); TESTASSERT(n == '1');
} catch (choice_details::bad_choice_access& e) { } catch (bad_choice_access& e) {
catched = true; catched = true;
} }
TESTASSERT(catched); TESTASSERT(catched);
@ -152,10 +159,26 @@ int test_choice()
TESTASSERT(C::counter == 1); TESTASSERT(C::counter == 1);
TESTASSERT(D::counter == 1); TESTASSERT(D::counter == 1);
// TEST: Move ctor
choice_t<char, int, double, C, D> c5{std::move(c3)}; choice_t<char, int, double, C, D> c5{std::move(c3)};
TESTASSERT(C::counter == 2); TESTASSERT(C::counter == 2);
choice_t<char, int, double, C, D> c6{std::move(c4)}; choice_t<char, int, double, C, D> c6{std::move(c4)};
TESTASSERT(D::counter == 2); TESTASSERT(D::counter == 2);
// TEST: move assignment
c = std::move(c3);
TESTASSERT(c.is<C>());
TESTASSERT(c3.is<C>() and c.holds_same_type(c3)); // move is not destructive
TESTASSERT(C::counter == 3);
c = std::move(c4);
TESTASSERT(c.is<D>());
TESTASSERT(c4.is<D>() and c.holds_same_type(c4));
TESTASSERT(C::counter == 2);
TESTASSERT(D::counter == 3);
c = std::move(c2);
TESTASSERT(c.is<double>() and c2.is<double>() and c.holds_same_type(c2));
TESTASSERT(c.get<double>() == c2.get<double>());
TESTASSERT(C::counter == 2 and D::counter == 2);
} }
TESTASSERT(C::counter == 0); TESTASSERT(C::counter == 0);
TESTASSERT(D::counter == 0); TESTASSERT(D::counter == 0);
@ -163,11 +186,56 @@ int test_choice()
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }
} // namespace choice_details struct E {
} // namespace srslte srslte::unique_byte_buffer_t pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
};
struct EVisitor {
template <typename T>
void operator()(T&& t)
{
// do nothing
}
void operator()(E& e) { pdu = std::move(e.pdu); }
srslte::unique_byte_buffer_t pdu;
};
int test_visit()
{
using srslte::choice_t;
choice_t<int, double, char, E> c{5};
// TEST: visitor hits integer type which is noop
EVisitor v;
srslte::visit(c, v);
TESTASSERT(c.is<int>() and c.get<int>() == 5);
TESTASSERT(v.pdu == nullptr);
// TEST: visitor hits type E and steals pdu
E e;
e.pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
c = std::move(e);
TESTASSERT(c.is<E>() and c.get<E>().pdu != nullptr);
srslte::visit(c, v);
TESTASSERT(v.pdu != nullptr);
TESTASSERT(c.is<E>() and c.get<E>().pdu == nullptr);
// TEST: visitor hits type E and steals pdu. Second type called there is no pdu to steal.
v.pdu = nullptr;
e.pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
c = std::move(e);
c.visit(v);
TESTASSERT(v.pdu != nullptr);
c.visit(v);
TESTASSERT(v.pdu == nullptr);
return SRSLTE_SUCCESS;
}
int main() int main()
{ {
TESTASSERT(srslte::choice_details::test_tagged_union() == SRSLTE_SUCCESS); TESTASSERT(test_tagged_union() == SRSLTE_SUCCESS);
TESTASSERT(srslte::choice_details::test_choice() == SRSLTE_SUCCESS); TESTASSERT(test_choice() == SRSLTE_SUCCESS);
TESTASSERT(test_visit() == SRSLTE_SUCCESS);
} }