Skip to content
Snippets Groups Projects
Commit 34304c3e authored by Carsten Gräser's avatar Carsten Gräser
Browse files

[!664] Allow returning references in Std::visit()

Merge branch 'introduce-lfe-variant' into 'master'

ref:core/dune-common Our own fallback implementation of Std::visit is
currently not compatible with visitors returning lvalues. This is needed for
the LocalFiniteElementVariant MR in dune-localfunctions. Notice that this also
avoids an allocation that was used in Std::visit before.

For the record: A further cleanup could optimize Hybrid::switchCase to use a
real c switch statement, because there's rumors that compiler can optimize
this much better than an `if ... else {} if...` sequence (current
implementation) or an array of fuction pointers (another possible visit
implementation).

See merge request [!664]

  [!664]: gitlab.dune-project.org/core/dune-common/merge_requests/664
parents 9c8ff584 1ab4d91f
Branches
Tags
1 merge request!664Allow returning references in Std::visit()
Pipeline #18306 passed
......@@ -338,55 +338,21 @@ namespace Impl {
* 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;
});
decltype(auto) visit(F&& func) {
auto dummyElseBranch = [&]() -> decltype(auto) { return func(this->get<0>());};
auto indices = std::make_index_sequence<size_>{};
return Hybrid::switchCases(indices, index(), [&](auto staticIndex) -> decltype(auto) {
return func(this->template get<decltype(staticIndex)::value>());
}, dummyElseBranch);
}
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;
});
decltype(auto) visit(F&& func) const {
auto dummyElseBranch = [&]() -> decltype(auto) { return func(this->get<0>());};
auto indices = std::make_index_sequence<size_>{};
return Hybrid::switchCases(indices, index(), [&](auto staticIndex) -> decltype(auto) {
return func(this->template get<decltype(staticIndex)::value>());
}, dummyElseBranch);
}
/** \brief Check if a given type is the one that is currently active in the variant. */
......@@ -428,12 +394,12 @@ namespace Impl {
}
template<typename F, typename... T>
auto visit(F&& visitor, variant<T...>& var) {
decltype(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) {
decltype(auto) visit(F&& visitor, const variant<T...>& var) {
return var.visit(std::forward<F>(visitor));
}
......
......@@ -87,6 +87,15 @@ Dune::TestSuite testVariant() {
suite.check(Std::visit(size, constv2)== 2, "Test const visit");
suite.check(Std::get_if<V2>(&constv2) != nullptr, "Test const get_if");
// Check visitor returning lvalue
using A = struct { int j; int i;};
using B = struct { int i;};
Std::variant<A,B> variant3;
variant3 = A{1,2};
Std::visit([](auto&& value) -> decltype(auto) { return (value.i); }, variant3) = 42;
suite.check(Std::visit([](auto&& value) { return value.i;}, variant3)== 42, "Std::visit returning lvalue");
/// test copy and move construction/assignment
{
auto variant_copy_constructed = variant2;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment