Skip to content
Snippets Groups Projects
Commit 5706c70d authored by akbib's avatar akbib
Browse files

Merge branch 'feature/fix-variant' into 'master'

Fix variant fallback

See merge request !7
parents e1d0e5a5 03fb576c
Branches
No related tags found
1 merge request!7Fix variant fallback
Pipeline #
...@@ -11,6 +11,9 @@ namespace Dune { ...@@ -11,6 +11,9 @@ namespace Dune {
namespace Std { namespace Std {
namespace Impl { namespace Impl {
// indicator value if something's not yet (or not any longer) valid
constexpr const auto invalidIndex = std::numeric_limits<size_t>::max();
/* helper constructs to find position of a type T in a pack Ts... */ /* helper constructs to find position of a type T in a pack Ts... */
template <typename T, typename... Ts> template <typename T, typename... Ts>
struct index_in_pack; struct index_in_pack;
...@@ -63,6 +66,9 @@ namespace Impl { ...@@ -63,6 +66,9 @@ namespace Impl {
const auto& get() const { const auto& get() const {
return tp_; return tp_;
} }
void reset() {};
private: private:
Tp tp_; Tp tp_;
}; };
...@@ -78,6 +84,8 @@ namespace Impl { ...@@ -78,6 +84,8 @@ namespace Impl {
::new (&tp_) Tp(std::forward<Args>(args)...); ::new (&tp_) Tp(std::forward<Args>(args)...);
} }
TypeStorage_() = delete;
auto& get() { auto& get() {
return *(tp_.ptr()); return *(tp_.ptr());
} }
...@@ -85,12 +93,23 @@ namespace Impl { ...@@ -85,12 +93,23 @@ namespace Impl {
return *(tp_.ptr()); return *(tp_.ptr());
} }
void reset() {
// Properly destruct the member:
tp_.ptr()->~Tp();
// (the memory itself doesn't need to be free'd. This is done when the Buffer_ member gets destructed)
}
private: private:
Buffer_<Tp> tp_; Buffer_<Tp> tp_;
}; };
template<typename... T> template<typename... T>
union variant_union_ {}; union variant_union_ {
// dummy (this should never be called)
void resetByIndex(size_t) {
assert(false);
};
};
template<typename Head_, typename... Tail_> template<typename Head_, typename... Tail_>
union variant_union_<Head_, Tail_...> { union variant_union_<Head_, Tail_...> {
...@@ -124,11 +143,22 @@ namespace Impl { ...@@ -124,11 +143,22 @@ namespace Impl {
return tail_.getByIndex(std::integral_constant<size_t, N-1>()); return tail_.getByIndex(std::integral_constant<size_t, N-1>());
} }
void resetByIndex(size_t indexToReset) {
if (indexToReset == 0) {
head_.reset();
return;
}
else {
tail_.resetByIndex(indexToReset-1);
}
}
template<typename Tp> template<typename Tp>
void set(Tp obj) { void set(Tp&& obj) {
Dune::Hybrid::ifElse(std::is_same<Tp, Head_>(), using T = std::decay_t<Tp>;
[&](auto&& id) { head_=std::move(id(obj)); }, Dune::Hybrid::ifElse(std::is_same<T, Head_>(),
[&](auto&& id) { return id(tail_).set(std::move(obj)); } [&](auto&& id) { id(head_)=std::forward<Tp>(obj); },
[&](auto&& id) { return id(tail_).set(std::forward<Tp>(obj)); }
); );
} }
...@@ -137,7 +167,7 @@ namespace Impl { ...@@ -137,7 +167,7 @@ namespace Impl {
} }
private: private:
TypeStorage_<Head_, std::is_literal_type<Head_>::value> head_; TypeStorage_<Head_, std::is_trivial<Head_>::value> head_;
variant_union_<Tail_...> tail_; variant_union_<Tail_...> tail_;
}; };
...@@ -145,7 +175,8 @@ namespace Impl { ...@@ -145,7 +175,8 @@ namespace Impl {
struct variant_{ struct variant_{
constexpr variant_() : constexpr variant_() :
unions_() {} unions_(),
index_(invalidIndex) {}
template<typename Tp> template<typename Tp>
constexpr variant_(Tp obj) : constexpr variant_(Tp obj) :
...@@ -155,6 +186,45 @@ namespace Impl { ...@@ -155,6 +186,45 @@ namespace Impl {
unions_.set(std::move(obj)); unions_.set(std::move(obj));
} }
variant_(variant_&& other) {
unions_ = std::move(other.unions_);
index_ = other.index_;
other.index_ = invalidIndex;
}
variant_(const variant_& other) {
index_ = other.index_;
namespace H = Dune::Hybrid;
H::forEach(H::integralRange(std::integral_constant<size_t, size_>()), [&](auto i) {
if(i==index_)
unions_.set(other.get<i>());
});
}
variant_& operator=(const variant_& other) {
if(index_ != invalidIndex)
unions_.resetByIndex(index_);
index_ = other.index_;
namespace H = Dune::Hybrid;
H::forEach(H::integralRange(std::integral_constant<size_t, size_>()), [&](auto i) {
if(i==index_)
unions_.set(other.get<i>());
});
return *this;
}
variant_& operator=(variant_&& other) {
unions_ = std::move(other.unions_);
index_ = other.index_;
other.index_ = invalidIndex;
return *this;
}
template<typename Tp> template<typename Tp>
auto& get() { auto& get() {
constexpr size_t idx = index_in_pack<Tp, T...>::value; constexpr size_t idx = index_in_pack<Tp, T...>::value;
...@@ -209,21 +279,25 @@ namespace Impl { ...@@ -209,21 +279,25 @@ namespace Impl {
template<size_t N> template<size_t N>
auto& get() { auto& get() {
if (index_ != N) if (index_ != N || index_ == invalidIndex)
DUNE_THROW(Dune::Exception, "Bad variant access."); DUNE_THROW(Dune::Exception, "Bad variant access.");
return unions_.template getByIndex(std::integral_constant<size_t, N>()); return unions_.template getByIndex(std::integral_constant<size_t, N>());
} }
template<size_t N> template<size_t N>
const auto& get() const { const auto& get() const {
if (index_ != N) if (index_ != N || index_ == invalidIndex)
DUNE_THROW(Dune::Exception, "Bad variant access."); DUNE_THROW(Dune::Exception, "Bad variant access.");
return unions_.template getByIndex(std::integral_constant<size_t, N>()); return unions_.template getByIndex(std::integral_constant<size_t, N>());
} }
template<typename Tp> template<typename Tp>
constexpr Tp& operator=(Tp obj) { constexpr Tp& operator=(Tp obj) {
unions_.set(std::move(obj));
constexpr auto index = index_in_pack<Tp, T...>::value; constexpr auto index = index_in_pack<Tp, T...>::value;
// before setting a new object into the buffer, we have to destruct the old element
if(not (index_ == index or index_ == invalidIndex)) {
unions_.resetByIndex(index_);
}
unions_.set(std::move(obj));
index_=index; index_=index;
return unions_.getByIndex(std::integral_constant<size_t,index>()); return unions_.getByIndex(std::integral_constant<size_t,index>());
} }
...@@ -236,6 +310,12 @@ namespace Impl { ...@@ -236,6 +310,12 @@ namespace Impl {
return sizeof...(T); return sizeof...(T);
} }
~variant_() {
if (index_ != invalidIndex) {
unions_.resetByIndex(index_);
}
}
/* \brief Apply visitor to the active variant. /* \brief Apply visitor to the active variant.
* *
* visit assumes that the result of * visit assumes that the result of
......
...@@ -83,6 +83,33 @@ Dune::TestSuite testVariant() { ...@@ -83,6 +83,33 @@ Dune::TestSuite testVariant() {
suite.check(Std::visit(size, constv2)== 2, "Test const visit"); suite.check(Std::visit(size, constv2)== 2, "Test const visit");
suite.check(Std::get_if<V2>(constv2) != nullptr, "Test const get_if"); suite.check(Std::get_if<V2>(constv2) != nullptr, "Test const get_if");
/// test copy and move construction/assignment
{
auto variant_copy_constructed = variant2;
// test if deep copy happened
auto getPtrToData = [&](const auto& vec) {return static_cast<const void*>(vec.data()); };
suite.check(Std::visit(getPtrToData, variant_copy_constructed) != Std::visit(getPtrToData, variant2), "Check deep copy") << "Both vector copies point to same data";
auto variant_move_constructed = std::move(variant_copy_constructed);
// TODO: Add sensible test condition here.
// Testing if the pointer to the data is the same as before is probably not a good idea,
// as moving does not imply that the data really stays at the same place (though it probably
// does).
// At least if this compiles and runs we can be confident no double frees happened.
//
// First idea: Test if the state looks somewhat valid
suite.check(Std::holds_alternative<V2>(variant_move_constructed), "Check if move constructed variant holds the right type");
Std::variant<V, V2> variant_copy_assigned;
variant_copy_assigned = variant2;
// test if deep copy happened
suite.check(Std::visit(getPtrToData, variant_copy_assigned) != Std::visit(getPtrToData, variant2), "Check deep copy at operator=") << "Both vector copies point to same data";
Std::variant<V, V2> variant_move_assigned;
variant_move_assigned = std::move(variant_copy_assigned);
// TODO: Again, as above, find a better test for this.
suite.check(Std::holds_alternative<V2>(variant_move_assigned), "Check if move assigned variant holds the right type");
}
return suite; return suite;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment