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

Add utilities for working with `std::reference_wrapper`

This adds three utilities (stolen from dune-functions) and a
corresponding test for working with `std::reference_wrapper`:

* `IsReferenceWrapper_v<T>` allows to check if `T` is an instantiation of `std::reference_wrapper`
* The function `resolveRef(t)` either returns `t.get()` or `t`
  depending on whether `t` is a `std::reference_wrapper` or a plain
  l-value reference.
* `ResolveRef_t<T>` provides the resolved type for `T`.

These utilities allow to support storing data members of classes by value _or_
reference transparently suing the following pattern. The class always stores
values, but supports passing `std::ref(...)` to opt-in store-by-reference.
Handling the latter can be done without boilerplate code using the
provided utilities:

```cpp
template<class StoredT>
struct SomeWrapper {

  using T = ResolveRef_t<StoredT>;

  SomeWrapper(const StoredT& t) : t_(t) {}

  void callFoo() const {
    resolveRef(t_).foo();
  }

  StoredT t_;
};

[...]

// Store t by value
auto w1 = SomeWrapper(t);
w1.callFoo();

// Store t by reference
auto w2 = SomeWrapper(std::cref(t));
w2.callFoo();
```
parent 5d96cbcc
Branches
Tags
1 merge request!1100Add utilities for working with `std::reference_wrapper`
Pipeline #43614 passed
Pipeline: Dune Nightly Test

#43616

    # Master (will become release 2.9)
    - Add helper function `resolveRef()` to transparently use `std::reference_wrapper`.
    - Add `pragma omp simd` annotations in the LoopSIMD class to improve compiler optimizations
    - deprecate Factorial in common/math.hh (use factorial function)
    ......
    ......@@ -80,6 +80,7 @@ install(FILES
    proxymemberaccess.hh
    quadmath.hh
    rangeutilities.hh
    referencehelper.hh
    reservedvector.hh
    scalarvectorview.hh
    scalarmatrixview.hh
    ......
    // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
    // vi: set et ts=4 sw=2 sts=2:
    #ifndef DUNE_COMMON_REFERENCE_HELPER_HH
    #define DUNE_COMMON_REFERENCE_HELPER_HH
    #include <type_traits>
    #include <functional>
    namespace Dune {
    namespace Impl {
    template<class T>
    class IsReferenceWrapper : public std::false_type {};
    template<class T>
    class IsReferenceWrapper<std::reference_wrapper<T>> : public std::true_type {};
    template<class T>
    class IsReferenceWrapper<const std::reference_wrapper<T>> : public std::true_type {};
    } // namespace Dune::Impl
    /**
    * \brief Helper to detect if given type is a std::reference_wrapper
    *
    * \ingroup CxxUtilities
    */
    template<class T>
    constexpr bool IsReferenceWrapper_v = Impl::IsReferenceWrapper<T>::value;
    /**
    * \brief Helper function to resolve std::reference_wrapper
    *
    * This is the overload for plain (mutable or const) l-value reference types.
    * It simply forwards the passed l-value reference.
    *
    * \ingroup CxxUtilities
    */
    template<class T>
    constexpr T& resolveRef(T& gf) noexcept
    {
    return gf;
    }
    // There's no overload for non std::reference_wrapper r-values,
    // because this may lead to undefined behavior whenever the
    // return value is stored.
    // Notice that deleting the overload is not necessary, but
    // helps to document that it is missing on purpose. It also
    // leads to nicer error messages.
    template<class T>
    const auto& resolveRef(T&& gf) = delete;
    /**
    * \brief Helper function to resolve std::reference_wrapper
    *
    * This is the overload for std::reference_wrapper<T>.
    * It resolves the reference by returning the stored
    * (mutable or const) l-value reference. It is safe
    * to call this with mutable or cost l-values
    * as well as r-values of std::reference_wrapper,
    * because the life time of the wrapped l-value reference
    * is independent of the wrapping std::reference_wrapper<T>
    * object.
    *
    * Notice that the copy created by calling this function
    * is easily elided by the compiler, since std::reference_wrapper
    * is trivially copyable.
    *
    * \ingroup CxxUtilities
    */
    template<class T>
    constexpr T& resolveRef(std::reference_wrapper<T> gf) noexcept
    {
    return gf.get();
    }
    /**
    * \brief Type trait to resolve std::reference_wrapper
    *
    * This is an alias for result of resolveRef.
    * Plain types T or const T are forwarded while
    * for T=std::reference_wrapper<S> the wrapped
    * type S is returned.
    *
    * \ingroup Utility
    */
    template<class T>
    using ResolveRef_t = std::remove_reference_t<decltype(resolveRef(std::declval<T&>()))>;
    } // namespace Dune
    #endif // DUNE_COMMON_REFERENCE_HELPER_HH
    ......@@ -298,6 +298,10 @@ dune_add_test(SOURCES rangeutilitiestest.cc
    LINK_LIBRARIES dunecommon
    LABELS quick)
    dune_add_test(SOURCES referencehelpertest.cc
    LINK_LIBRARIES dunecommon
    LABELS quick)
    dune_add_test(SOURCES reservedvectortest.cc
    LINK_LIBRARIES dunecommon
    LABELS quick)
    ......
    // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
    // vi: set et ts=4 sw=2 sts=2:
    #include <config.h>
    #include <functional>
    #include <dune/common/parallel/mpihelper.hh>
    #include <dune/common/referencehelper.hh>
    #include <dune/common/test/testsuite.hh>
    class CopyCounter
    {
    public:
    CopyCounter() : count_(0) {}
    CopyCounter(std::size_t count) : count_(count) {}
    CopyCounter(const CopyCounter& other) :
    count_(other.count_ + 1)
    {}
    auto& getCount() {
    return count_;
    }
    const auto& getCount() const {
    return count_;
    }
    void setCount(std::size_t count) {
    count_ = count;
    }
    private:
    mutable std::size_t count_;
    };
    int main (int argc, char *argv[]) try
    {
    // Set up MPI, if available
    Dune::MPIHelper::instance(argc, argv);
    Dune::TestSuite suite;
    suite.subTest([]() {
    Dune::TestSuite subSuite("Checking with mutable l-value");
    CopyCounter c;
    Dune::resolveRef(c).setCount(42);
    Dune::resolveRef(c).getCount();
    subSuite.check(Dune::resolveRef(c).getCount() == 42, "Checking resolveRef");
    subSuite.check(not Dune::IsReferenceWrapper_v<decltype(c)>, "Checking IsReferenceWrapper_v");
    return subSuite;
    }());
    suite.subTest([]() {
    Dune::TestSuite subSuite("Checking with const l-value");
    const CopyCounter c(42);
    Dune::resolveRef(c).getCount();
    subSuite.check(Dune::resolveRef(c).getCount() == 42, "Checking resolveRef");
    subSuite.check(not Dune::IsReferenceWrapper_v<decltype(c)>, "Checking IsReferenceWrapper_v");
    return subSuite;
    }());
    suite.subTest([]() {
    Dune::TestSuite subSuite("Checking with mutable reference_wrapper of mutable l-value");
    CopyCounter c;
    auto c_ref = std::ref(c);
    Dune::resolveRef(c_ref).setCount(42);
    Dune::resolveRef(c_ref).getCount();
    subSuite.check(Dune::resolveRef(c_ref).getCount() == 42, "Checking resolveRef");
    subSuite.check(Dune::IsReferenceWrapper_v<decltype(c_ref)>, "Checking IsReferenceWrapper_v");
    return subSuite;
    }());
    suite.subTest([]() {
    Dune::TestSuite subSuite("Checking with const reference_wrapper of mutable l-value");
    CopyCounter c;
    const auto c_ref = std::ref(c);
    Dune::resolveRef(c_ref).setCount(42);
    Dune::resolveRef(c_ref).getCount();
    subSuite.check(Dune::resolveRef(c_ref).getCount() == 42, "Checking resolveRef");
    subSuite.check(Dune::IsReferenceWrapper_v<decltype(c_ref)>, "Checking IsReferenceWrapper_v");
    return subSuite;
    }());
    suite.subTest([]() {
    Dune::TestSuite subSuite("Checking with mutable reference_wrapper of const l-value");
    const CopyCounter c(42);
    auto c_ref = std::ref(c);
    Dune::resolveRef(c_ref).getCount();
    subSuite.check(Dune::resolveRef(c_ref).getCount() == 42, "Checking resolveRef");
    subSuite.check(Dune::IsReferenceWrapper_v<decltype(c_ref)>, "Checking IsReferenceWrapper_v");
    return subSuite;
    }());
    suite.subTest([]() {
    Dune::TestSuite subSuite("Checking with const reference_wrapper of const l-value");
    const CopyCounter c(42);
    const auto c_ref = std::ref(c);
    Dune::resolveRef(c_ref).getCount();
    subSuite.check(Dune::resolveRef(c_ref).getCount() == 42, "Checking resolveRef");
    subSuite.check(Dune::IsReferenceWrapper_v<decltype(c_ref)>, "Checking IsReferenceWrapper_v");
    return subSuite;
    }());
    suite.subTest([]() {
    Dune::TestSuite subSuite("Checking with const reference_wrapper of const l-value (via std::cref)");
    CopyCounter c(42);
    auto c_ref = std::cref(c);
    Dune::resolveRef(c_ref).getCount();
    subSuite.check(Dune::resolveRef(c_ref).getCount() == 42, "Checking resolveRef");
    subSuite.check(Dune::IsReferenceWrapper_v<decltype(c_ref)>, "Checking IsReferenceWrapper_v");
    return subSuite;
    }());
    suite.subTest([]() {
    Dune::TestSuite subSuite("Checking with const reference_wrapper r-value of mutable l-value");
    CopyCounter c;
    Dune::resolveRef(std::ref(c)).setCount(42);
    Dune::resolveRef(std::ref(c)).getCount();
    subSuite.check(Dune::resolveRef(std::ref(c)).getCount() == 42, "Checking resolveRef");
    subSuite.check(Dune::IsReferenceWrapper_v<decltype(std::ref(c))>, "Checking IsReferenceWrapper_v");
    return subSuite;
    }());
    return suite.exit();
    }
    catch (std::exception& e) {
    std::cout << e.what() << std::endl;
    return 1;
    }
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment