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

Add std::variant fallback

This is a fallback implementation of C++17's std::variant. It does not model the complete
STL interface but the basis functionality is there. There are probably many corner cases this
implementation can not handle yet.
This should probably be in dune-common, but for now, I'll put it here.
parent e4d207d6
No related branches found
No related tags found
No related merge requests found
add_subdirectory(common)
add_subdirectory(facetools) add_subdirectory(facetools)
add_subdirectory(subgrid) add_subdirectory(subgrid)
add_subdirectory(test) 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 {
/* 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 = typename std::aligned_storage_t<sizeof(Tp)>::type;
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_;
}
private:
Tp tp_;
};
template<typename Tp>
struct TypeStorage_<Tp, false> {
TypeStorage_(Tp t) {
//tp_ = ::new Tp(t);
::new (&tp_) Tp(t);
}
template<typename... Args>
TypeStorage_(Args... args) {
::new (&tp_) Tp(std::forward<Args>(args)...);
}
auto& get() {
return *(tp_.ptr());
}
const auto& get() const {
return *(tp_.ptr());
}
private:
Buffer_<Tp> tp_;
};
template<typename... T>
union variant_union_ {};
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)...) {}
// TODO: This should not be a copy! However, if I return by reference,
// compiler tells me it can not return an temporary as a non-const ref. (which is of course
// true, but I don't see why this is a temporary?). Then again, one does not need this
// function anyway. Probably one should just drop it.
template<typename Tp>
auto getByType() {
return Dune::Hybrid::ifElse(std::is_same<Tp, Head_>(),
[this](auto) { return this->head_.get();},
[this](auto id) { return id(this->tail_).template getByType<Tp>();}
);
}
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>());
}
template<typename Tp>
void set(Tp obj) {
Dune::Hybrid::ifElse(std::is_same<Tp, Head_>(),
[&](auto&& id) { head_=std::move(id(obj)); },
[&](auto&& id) { return id(tail_).set(std::move(obj)); }
);
}
constexpr size_t size() const {
return sizeof...(Tail_)+1;
}
private:
TypeStorage_<Head_, std::is_literal_type<Head_>::value> head_;
variant_union_<Tail_...> tail_;
};
template<typename...T>
struct variant_{
constexpr variant_() :
unions_() {}
template<typename Tp>
constexpr variant_(Tp obj) :
unions_(),
index_(index_in_pack<Tp, T...>::value)
{
unions_.set(std::move(obj));
}
template<typename Tp>
auto& get() {
constexpr size_t idx = index_in_pack<Tp, T...>::value;
return get<idx>();
}
template<typename Tp>
const auto& get() const {
constexpr size_t idx = index_in_pack<Tp, T...>::value;
return get<idx>();
}
template<size_t N>
auto& get() {
return unions_.template getByIndex(std::integral_constant<size_t, N>());
}
template<size_t N>
const auto& get() const {
return unions_.template getByIndex(std::integral_constant<size_t, N>());
}
template<typename Tp>
constexpr Tp& operator=(Tp obj) {
unions_.set(std::move(obj));
constexpr auto index = index_in_pack<Tp, T...>::value;
index_=index;
return unions_.getByIndex(std::integral_constant<size_t,index>());
}
constexpr std::size_t index() const {
return index_;
}
constexpr auto size() const {
return sizeof...(T);
}
/* \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_);
}
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>
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
set(TESTPROGS test-w-onedgrid test-w-yaspgrid test-w-alugrid) set(TESTPROGS test-w-onedgrid test-w-yaspgrid test-w-alugrid testvariant)
if(ALBERTA_FOUND) if(ALBERTA_FOUND)
list(APPEND TESTPROGS test-w-albertagrid2d) 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) {}
F& operator *=(int factor) {
i*=factor;
return *this;
}
};
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);
variant = d;
suite.check(Std::holds_alternative<double>(variant));
variant = f;
suite.check(Std::holds_alternative<F>(variant));
variant = i;
suite.check(Std::holds_alternative<int>(variant));
// TODO: actual compare operators
suite.check(Std::get<int>(variant) == i);
suite.check(Std::get<0>(variant) == i);
variant = V(1);
suite.check(Std::get<V>(variant).size() == 1);
variant = f;
suite.check(variant.index() == 2); // we're at type F, which has position 2
// Demonstrate visit concept:
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);
variant2 = V2(2);
suite.check(Std::visit(size, variant2)== 2);
// try on a const reference:
const auto& constv2 = variant2;
suite.check(Std::visit(size, constv2)== 2);
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.
Please register or to comment