Skip to content
Snippets Groups Projects
Commit f003dc93 authored by lh1887's avatar lh1887
Browse files

Remove std::variant fallback

The implementation is now part of dune-common and maintained there.
parent e0a3ab0c
No related branches found
No related tags found
1 merge request!14Remove std::variant fallback
Pipeline #15600 failed
add_subdirectory(common)
add_subdirectory(facetools)
add_subdirectory(subgrid)
add_subdirectory(test)
......
set(commonincludedir ${CMAKE_INSTALL_INCLUDEDIR}/dune/subgrid/common)
set(commoninclude_HEADERS variant.hh)
# include not needed for CMake
# include $(top_srcdir)/am/global-rules
install(FILES ${commoninclude_HEADERS} DESTINATION ${commonincludedir})
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#ifndef DUNE_COMMON_STD_VARIANT_HH
#define DUNE_COMMON_STD_VARIANT_HH
#include <tuple>
#include <memory>
#include <dune/common/hybridutilities.hh>
#include <dune/common/exceptions.hh>
namespace Dune {
namespace Std {
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... */
template <typename T, typename... Ts>
struct index_in_pack;
template <typename T, typename... Ts>
struct index_in_pack<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename U, typename... Ts>
struct index_in_pack<T, U, Ts...> : std::integral_constant<std::size_t, 1 + index_in_pack<T, Ts...>::value> {};
/* end helper constructs to find position of a type T in a pack Ts... */
template<typename Tp>
struct Buffer_ : std::aligned_storage<sizeof(Tp)> {
using Storage = std::aligned_storage_t<sizeof(Tp)>;
Storage storage_;
void* addr() {
return static_cast<void*>(&storage_);
}
const void* addr() const {
return static_cast<const void*>(&storage_);
}
Tp* ptr() {
return static_cast<Tp*>(addr());
}
const Tp* ptr() const {
return static_cast<const Tp*>(addr());
}
};
template<typename Tp, bool isTrivial>
struct TypeStorage_ { };
template<typename Tp>
struct TypeStorage_<Tp, true> {
TypeStorage_(Tp t) :
tp_(t) {}
template<typename... Args>
TypeStorage_(Args... args) :
tp_(args...) {}
auto& get() {
return tp_;
}
const auto& get() const {
return tp_;
}
void reset() {};
private:
Tp tp_;
};
template<typename Tp>
struct TypeStorage_<Tp, false> {
TypeStorage_(Tp t) {
::new (&tp_) Tp(t);
}
template<typename... Args>
TypeStorage_(Args... args) {
::new (&tp_) Tp(std::forward<Args>(args)...);
}
TypeStorage_() = delete;
auto& get() {
return *(tp_.ptr());
}
const auto& get() const {
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:
Buffer_<Tp> tp_;
};
template<typename... T>
union variant_union_ {
// dummy (this should never be called)
void resetByIndex(size_t) {
assert(false);
};
};
template<typename Head_, typename... Tail_>
union variant_union_<Head_, Tail_...> {
constexpr variant_union_() :
tail_() {}
template<typename... Args>
constexpr variant_union_(std::integral_constant<size_t, 0>, Args&&... args) :
head_(std::forward<Args...>(args)...) {}
template<size_t N, typename... Args>
constexpr variant_union_(std::integral_constant<size_t, N>, Args&&... args) :
tail_(std::integral_constant<size_t, N-1>(), std::forward<Args...>(args)...) {}
auto& getByIndex(std::integral_constant<size_t, 0>) {
return head_.get();
}
const auto& getByIndex(std::integral_constant<size_t, 0>) const {
return head_.get();
}
template<size_t N>
auto& getByIndex(std::integral_constant<size_t, N>) {
return tail_.getByIndex(std::integral_constant<size_t, N-1>());
}
template<size_t N>
const auto& getByIndex(std::integral_constant<size_t, N>) const {
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>
void set(Tp&& obj) {
using T = std::decay_t<Tp>;
Dune::Hybrid::ifElse(std::is_same<T, Head_>(),
[&](auto&& id) { id(head_)=std::forward<Tp>(obj); },
[&](auto&& id) { return id(tail_).set(std::forward<Tp>(obj)); }
);
}
constexpr size_t size() const {
return sizeof...(Tail_)+1;
}
private:
TypeStorage_<Head_, std::is_trivial<Head_>::value> head_;
variant_union_<Tail_...> tail_;
};
template<typename...T>
struct variant_{
constexpr variant_() :
unions_(),
index_(invalidIndex) {}
template<typename Tp>
constexpr variant_(Tp obj) :
unions_(),
index_(index_in_pack<Tp, T...>::value)
{
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.template 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.template get<i>());
});
return *this;
}
variant_& operator=(variant_&& other) {
unions_ = std::move(other.unions_);
index_ = other.index_;
other.index_ = invalidIndex;
return *this;
}
template<typename Tp>
auto& get() {
constexpr size_t idx = index_in_pack<Tp, T...>::value;
if (index_ != idx)
DUNE_THROW(Dune::Exception, "Bad variant access.");
return get<idx>();
}
template<typename Tp>
const auto& get() const {
constexpr size_t idx = index_in_pack<Tp, T...>::value;
if (index_ != idx)
DUNE_THROW(Dune::Exception, "Bad variant access.");
return get<idx>();
}
template<typename Tp>
Tp* get_if() {
if (not holds_alternative<Tp>())
return (Tp*) nullptr;
else
return &(get<Tp>());
}
template<typename Tp>
const Tp* get_if() const {
if (not holds_alternative<Tp>())
return (Tp*) nullptr;
else
return &(get<Tp>());
}
template<size_t N>
auto* get_if() {
using Tp = std::decay_t<decltype(get<N>())>;
if (not holds_alternative<N>())
return (Tp*) nullptr;
else
return &(get<Tp>());
}
template<size_t N>
const auto* get_if() const {
using Tp = std::decay_t<decltype(get<N>())>;
if (not holds_alternative<N>())
return (Tp*) nullptr;
else
return &(get<Tp>());
}
template<size_t N>
auto& get() {
if (index_ != N || index_ == invalidIndex)
DUNE_THROW(Dune::Exception, "Bad variant access.");
return unions_.template getByIndex(std::integral_constant<size_t, N>());
}
template<size_t N>
const auto& get() const {
if (index_ != N || index_ == invalidIndex)
DUNE_THROW(Dune::Exception, "Bad variant access.");
return unions_.template getByIndex(std::integral_constant<size_t, N>());
}
template<typename Tp>
constexpr Tp& operator=(Tp obj) {
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;
return unions_.getByIndex(std::integral_constant<size_t,index>());
}
constexpr std::size_t index() const noexcept {
return index_;
}
constexpr auto size() const {
return sizeof...(T);
}
~variant_() {
if (index_ != invalidIndex) {
unions_.resetByIndex(index_);
}
}
/* \brief Apply visitor to the active variant.
*
* visit assumes that the result of
* func(T) has the same type for all types T
* in this variant.
*/
template<typename F>
auto visit(F&& func) {
using namespace Dune::Hybrid;
using Result = decltype(func(unions_.getByIndex(std::integral_constant<size_t, 0>())));
return ifElse(std::is_same<Result, void>(), [&, this](auto id) {
constexpr auto tsize = size_;
Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&](auto i) {
if (i==this->index_)
func(id(unions_).getByIndex(std::integral_constant<size_t, i>()));
});
return;},
[&func,this](auto id) {
constexpr auto tsize = size_;
auto result = std::unique_ptr<Result>();
Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&, this](auto i) {
if (i==this->index_)
result = std::make_unique<Result>(func(id(this->unions_).getByIndex(std::integral_constant<size_t, i>())));
});
return *result;
});
}
template<typename F>
auto visit(F&& func) const {
using namespace Dune::Hybrid;
using Result = decltype(func(unions_.getByIndex(std::integral_constant<size_t, 0>())));
return ifElse(std::is_same<Result, void>(), [&, this](auto id) {
constexpr auto tsize = size_;
Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&](auto i) {
if (i==this->index_)
func(id(unions_).getByIndex(std::integral_constant<size_t, i>()));
});
return;},
[&func,this](auto id) {
constexpr auto tsize = size_;
auto result = std::unique_ptr<Result>();
Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&, this](auto i) {
if (i==this->index_)
result = std::make_unique<Result>(func(id(this->unions_).getByIndex(std::integral_constant<size_t, i>())));
});
return *result;
});
}
/** \brief Check if a given type is the one that is currently active in the variant. */
template<typename Tp>
constexpr bool holds_alternative() const {
// I have no idea how this could be really constexpr, but for STL-conformity,
// I'll leave the modifier there.
return (index_in_pack<Tp, T...>::value == index_);
}
/** \brief Check if a given type is the one that is currently active in the variant. */
template<size_t N>
constexpr bool holds_alternative() const {
// I have no idea how this could be really constexpr, but for STL-conformity,
// I'll leave the modifier there.
return (N == index_);
}
private:
variant_union_<T...> unions_;
std::size_t index_;
constexpr static auto size_ = sizeof...(T);
};
} // end namespace Impl
/** \brief Incomplete re-implementation of C++17's std::variant. */
template<typename ...T>
using variant = Impl::variant_<T...>;
template<size_t N, typename... T>
auto& get(variant<T...>& var) {
return var.template get<N>();
}
template<size_t N, typename... T>
const auto& get(const variant<T...>& var) {
return var.template get<N>();
}
template<typename F, typename... T>
auto visit(F&& visitor, variant<T...>& var) {
return var.visit(std::forward<F>(visitor));
}
template<typename F, typename... T>
auto visit(F&& visitor, const variant<T...>& var) {
return var.visit(std::forward<F>(visitor));
}
template<typename Tp, typename ...T>
auto& get(variant<T...>& var) {
return var.template get<Tp>();
}
template<typename Tp, typename ...T>
const auto& get(const variant<T...>& var) {
return var.template get<Tp>();
}
template<typename Tp, typename ...T>
const auto* get_if(const variant<T...>& var) {
return var.template get_if<Tp>();
}
template<typename Tp, typename ...T>
auto* get_if(variant<T...>& var) {
return var.template get_if<Tp>();
}
template<size_t N, typename ...T>
const auto* get_if(const variant<T...>& var) {
return var.template get_if<N>();
}
template<size_t N, typename ...T>
auto* get_if(variant<T...>& var) {
return var.template get_if<N>();
}
template<typename Tp, typename ...T>
constexpr bool holds_alternative(const variant<T...>& var) {
return var.template holds_alternative<Tp>();
}
template <typename... T>
constexpr auto variant_size_v(const variant<T...>&) {
return std::integral_constant<std::size_t,sizeof...(T)>::value;
}
} // end namespace Std
} // end namespace Dune
#endif
......@@ -5,7 +5,7 @@
* \brief The SubGridGeometry class and its specializations
*/
#include <type_traits>
#include <dune/subgrid/common/variant.hh>
#include <dune/common/std/variant.hh>
#include <dune/common/fmatrix.hh>
#include <dune/common/typetraits.hh>
......
set(TESTPROGS test-w-onedgrid test-w-yaspgrid test-w-alugrid testvariant)
set(TESTPROGS test-w-onedgrid test-w-yaspgrid test-w-alugrid)
if(ALBERTA_FOUND)
list(APPEND TESTPROGS test-w-albertagrid2d)
......
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <iostream>
#include <dune/common/parallel/mpihelper.hh> // An initializer of MPI
#include <dune/common/exceptions.hh> // We use exceptions
#include <dune/common/test/testsuite.hh>
#include <dune/subgrid/common/variant.hh>
// some non-default constructible type
struct F {
int i;
F() = delete;
F(int j) :
i(j) {}
};
Dune::TestSuite testVariant() {
using namespace Dune;
TestSuite suite;
int i = 42;
double d = 3.14;
F f(13);
using V = std::vector<int>;
auto variant = Std::variant<int, double, F, V>();
suite.check(Std::variant_size_v(variant) == 4, "Test variant_size_v");
variant = d;
suite.check(Std::holds_alternative<double>(variant), "Test holds_alternative");
variant = f;
suite.check(Std::holds_alternative<F>(variant), "Test holds_alternative");
variant = i;
suite.check(Std::holds_alternative<int>(variant), "Test holds_alternative");
suite.check(Std::get<int>(variant) == i, "Test get<Type>");
suite.check(Std::get<0>(variant) == i, "Test get<Index>");
suite.check(Std::get_if<int>(variant) != nullptr, "Test get_if on right type");
suite.check(Std::get_if<double>(variant) == nullptr, "Test get_if on wrong type");
suite.check(Std::get_if<0>(variant) != nullptr, "Test get_if on right index");
suite.check(Std::get_if<1>(variant) == nullptr, "Test get_if on wrong index");
// test if get<Type> throws if one tries to get the wrong type:
try {
// currently hold type is still int, so double should throw
Std::get<double>(variant);
suite.check(false, "Test get<Type> on wrong type should have thrown");
}
catch (...) {
suite.check(true, "Test get<Type> on wrong type has thrown");
}
variant = V(1);
suite.check(Std::get<V>(variant).size() == 1, "Test with non-trivial type");
variant = f;
suite.check(variant.index() == 2, "Test index()"); // we're at type F, which has position 2
// Demonstrate visit concept and using vector as an example of a non-POD type
using V2 = std::vector<double>;
Std::variant<V, V2> variant2;
variant2 = V(1);
auto size = [](auto&& v) {return v.size();};
suite.check(Std::visit(size, variant2)== 1, "Test visit");
variant2 = V2(2);
suite.check(Std::visit(size, variant2)== 2, "Test visit");
// try on a const reference:
const auto& constv2 = variant2;
suite.check(Std::visit(size, constv2)== 2, "Test const visit");
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;
}
int main(int argc, char** argv)
{
try{
// Maybe initialize MPI
Dune::MPIHelper::instance(argc, argv);
Dune::TestSuite suite;
suite.subTest(testVariant());
return suite.exit();
}
catch (Dune::Exception &e){
std::cerr << "Dune reported error: " << e << std::endl;
}
catch (...){
std::cerr << "Unknown exception thrown!" << std::endl;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment