Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for reference_wrappers in make_xxx #179

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions doc/tutorial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2034,21 +2034,40 @@ and so on.

@subsection tutorial-containers-elements Container elements

In Hana, containers own their elements. When a container is created, it makes
a _copy_ of the elements used to initialize it and stores them inside the
container. Of course, unnecessary copies are avoided by using move semantics.
Because of those owning semantics, the lifetime of the objects inside the
container is the same as that of the container.
In Hana, containers own their elements. When a container is created, it
normally makes a _copy_ of the elements used to initialize it and stores them
inside the container. Of course, unnecessary copies are avoided by using move
semantics. Because of those owning semantics, the lifetime of the objects
inside the container is the same as that of the container.

@snippet example/tutorial/containers.cpp lifetime

Much like containers in the standard library, containers in Hana expect their
elements to be objects. For this reason, references _may not_ be stored in
them. When references must be stored inside a container, one should use a
`std::reference_wrapper` instead:
However, some containers allow storing references instead of actual objects.
In that case, the owning semantics explained above do not hold anymore. For
example, it is possible to create a `hana::tuple` holding references as
follows:

@snippet example/tutorial/containers.cpp reference_tuple

@note
Of course, a single tuple can also hold some elements by value and other
elements by reference.

Since explicitly specifying the type of the container to achieve by-reference
semantics can be cumbersome (and sometimes downright impossible when that
type is implementation-defined), the `make_xxx` family of functions also
support the use of `reference_wrapper`s:

@snippet example/tutorial/containers.cpp reference_wrapper

When passed to a `hana::make_xxx` function, `std::reference_wrapper`s and
`boost::reference_wrapper`s will cause the container to hold a reference
instead of a `reference_wrapper`. Of course, only the `make_xxx` functions
associated to containers that support holding references implement this
special behavior. To know whether a container is able to hold references
(and implements this behavior), one should consult the reference documentation
for that container.




Expand Down
28 changes: 20 additions & 8 deletions example/tutorial/containers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,29 @@ std::string& s = xs[0_c];

}{

//! [reference_tuple]
std::string hello = "Hello";
std::vector<char> world = {'W', 'o', 'r', 'l', 'd'};

hana::tuple<std::string&, std::vector<char>&> xs{hello, world};

// s is a reference to `hello`
std::string& s = xs[0_c];
BOOST_HANA_RUNTIME_CHECK(&s == &hello);
//! [reference_tuple]

}{

//! [reference_wrapper]
std::vector<int> ints = { /* huge vector of ints */ };
std::vector<std::string> strings = { /* huge vector of strings */ };
std::string hello = "Hello";
std::vector<char> world = {'W', 'o', 'r', 'l', 'd'};

auto map = hana::make_map(
hana::make_pair(hana::type_c<int>, std::ref(ints)),
hana::make_pair(hana::type_c<std::string>, std::ref(strings))
);
auto xs = hana::make_tuple(std::ref(hello), std::ref(world));

auto& v = map[hana::type_c<int>].get();
BOOST_HANA_RUNTIME_CHECK(&v == &ints);
std::string& hello_ref = xs[0_c];
std::vector<char>& world_ref = xs[1_c];
BOOST_HANA_RUNTIME_CHECK(&hello_ref == &hello);
BOOST_HANA_RUNTIME_CHECK(&world_ref == &world);
//! [reference_wrapper]

}
Expand Down
81 changes: 81 additions & 0 deletions include/boost/hana/detail/as_container_element.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*!
@file
Defines `boost::hana::detail::as_container_element_t`.

@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#ifndef BOOST_HANA_DETAIL_AS_CONTAINER_ELEMENT_HPP
#define BOOST_HANA_DETAIL_AS_CONTAINER_ELEMENT_HPP

#include <boost/hana/config.hpp>
#include <boost/hana/detail/decay.hpp>


// We forward declare `std::reference_wrapper` because including the whole
// `<functional>` header is too expensive in terms of compile-time.
// Technically, this is disallowed by the standard, and we'll have to handle
// each standard library separately. See this issue for more information:
// https://github.com/ldionne/hana/issues/176.
//
// Dear Standard Committee, it is about time we get fine grained headers...
#if defined(BOOST_HANA_CONFIG_LIBCPP)

_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
class _LIBCPP_TYPE_VIS_ONLY reference_wrapper;
_LIBCPP_END_NAMESPACE_STD

#elif defined(BOOST_HANA_CONFIG_LIBSTDCXX)

namespace std _GLIBCXX_VISIBILITY(default) {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
template <class _Tp>
class reference_wrapper;
_GLIBCXX_END_NAMESPACE_VERSION
}

#elif defined(BOOST_HANA_CONFIG_LIBMSVCCXX)

namespace std {
template<class _Ty>
class reference_wrapper;
}

#else

# error "Hana must be taught how to forward declare std::reference_wrapper for your standard library"

#endif


// We also forward declare boost::reference_wrapper, because it's nice to
// interoperate with it too.
namespace boost { template <class T> class reference_wrapper; }


namespace boost { namespace hana { namespace detail {
template <typename T>
struct as_container_element_impl {
using type = T;
};

template <typename T>
struct as_container_element_impl<std::reference_wrapper<T>> {
using type = T&;
};

template <typename T>
struct as_container_element_impl<boost::reference_wrapper<T>> {
using type = T&;
};

template <typename T>
using as_container_element_t = typename as_container_element_impl<
typename detail::decay<T>::type
>::type;
}}} // end namespace boost::hana::detail

#endif // !BOOST_HANA_DETAIL_AS_CONTAINER_ELEMENT_HPP
4 changes: 4 additions & 0 deletions include/boost/hana/fwd/lazy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ BOOST_HANA_NAMESPACE_BEGIN
//! equal to `x`, and `make<lazy_tag>(f)(x1, ..., xN)` is a lazy function
//! call that is equal to `f(x1, ..., xN)` when it is `eval`uated.
//!
//! `make<lazy_tag>` supports reference wrappers. When a reference wrapper
//! is passed to it, the resulting `hana::lazy` will hold a reference
//! to the object instead of the object itself.
//!
//! @note
//! It is interesting to note that `make<lazy_tag>(f)(x1, ..., xN)` is
//! equivalent to
Expand Down
5 changes: 5 additions & 0 deletions include/boost/hana/fwd/optional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ BOOST_HANA_NAMESPACE_BEGIN
//! `make<optional_tag>(x)` is equivalent to `just(x)`. This is provided
//! for consistency with the other `make<...>` functions.
//!
//! @note
//! `make<optional_tag>` supports reference wrappers. When a reference
//! wrapper is passed to it, the resulting `hana::optional` will hold a
//! reference to the object instead of the object itself.
//!
//!
//! Example
//! -------
Expand Down
7 changes: 7 additions & 0 deletions include/boost/hana/fwd/pair.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ BOOST_HANA_NAMESPACE_BEGIN
//! @relates hana::pair
//!
//!
//! @note
//! `make<pair_tag>` supports reference wrappers. When a reference wrapper
//! is passed to it, the resulting `hana::pair` will hold a reference
//! to the object enclosed in the reference wrapper instead of the object
//! itself.
//!
//!
//! Example
//! -------
//! @include example/pair/make.cpp
Expand Down
7 changes: 5 additions & 2 deletions include/boost/hana/fwd/tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,11 @@ BOOST_HANA_NAMESPACE_BEGIN
//!
//! Given zero or more objects `xs...`, `make<tuple_tag>` returns a new tuple
//! containing those objects. The elements are held by value inside the
//! resulting tuple, and they are hence copied or moved in. This is
//! analogous to `std::make_tuple` for creating Hana tuples.
//! resulting tuple, and they are hence copied or moved in. However,
//! `make<tuple_tag>` supports reference wrappers. When a reference wrapper
//! is passed to it, the resulting `hana::tuple` will hold a reference
//! to the object enclosed in the reference wrapper instead of the object
//! itself.
//!
//!
//! Example
Expand Down
8 changes: 4 additions & 4 deletions include/boost/hana/lazy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Distributed under the Boost Software License, Version 1.0.
#include <boost/hana/basic_tuple.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/core/make.hpp>
#include <boost/hana/detail/decay.hpp>
#include <boost/hana/detail/as_container_element.hpp>
#include <boost/hana/detail/operators/adl.hpp>
#include <boost/hana/detail/operators/monad.hpp>
#include <boost/hana/functional/apply.hpp>
Expand Down Expand Up @@ -71,7 +71,7 @@ BOOST_HANA_NAMESPACE_BEGIN
template <typename ...Args>
constexpr lazy_apply_t<
std::make_index_sequence<sizeof...(Args)>,
X, typename detail::decay<Args>::type...
X, detail::as_container_element_t<Args>...
> operator()(Args&& ...args) const& {
return {detail::lazy_secret{},
hana::get_impl<0>(storage_), static_cast<Args&&>(args)...};
Expand All @@ -80,7 +80,7 @@ BOOST_HANA_NAMESPACE_BEGIN
template <typename ...Args>
constexpr lazy_apply_t<
std::make_index_sequence<sizeof...(Args)>,
X, typename detail::decay<Args>::type...
X, detail::as_container_element_t<Args>...
> operator()(Args&& ...args) && {
return {detail::lazy_secret{},
static_cast<X&&>(hana::get_impl<0>(storage_)),
Expand All @@ -95,7 +95,7 @@ BOOST_HANA_NAMESPACE_BEGIN
template <>
struct make_impl<lazy_tag> {
template <typename X>
static constexpr lazy_value_t<typename detail::decay<X>::type> apply(X&& x) {
static constexpr lazy_value_t<detail::as_container_element_t<X>> apply(X&& x) {
return {detail::lazy_secret{}, static_cast<X&&>(x)};
}
};
Expand Down
17 changes: 14 additions & 3 deletions include/boost/hana/optional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Distributed under the Boost Software License, Version 1.0.
#include <boost/hana/bool.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/core/tag_of.hpp>
#include <boost/hana/detail/as_container_element.hpp>
#include <boost/hana/detail/operators/adl.hpp>
#include <boost/hana/detail/operators/comparable.hpp>
#include <boost/hana/detail/operators/monad.hpp>
Expand Down Expand Up @@ -59,6 +60,9 @@ BOOST_HANA_NAMESPACE_BEGIN
constexpr optional(optional const&) = default;
constexpr optional(optional&&) = default;

template <typename U = T, typename = typename std::enable_if<
!std::is_reference<U>::value
>::type>
constexpr optional(T const& t)
: value_(t)
{ }
Expand All @@ -72,8 +76,15 @@ BOOST_HANA_NAMESPACE_BEGIN
constexpr optional& operator=(optional&&) = default;

// 5.3.5, Observers
constexpr T const* operator->() const { return &value_; }
constexpr T* operator->() { return &value_; }
template <typename U = T, typename = typename std::enable_if<
!std::is_reference<U>::value
>::type>
constexpr U const* operator->() const { return &value_; }

template <typename U = T, typename = typename std::enable_if<
!std::is_reference<U>::value
>::type>
constexpr U* operator->() { return &value_; }

constexpr T& value() & { return value_; }
constexpr T const& value() const& { return value_; }
Expand Down Expand Up @@ -115,7 +126,7 @@ BOOST_HANA_NAMESPACE_BEGIN

template <typename T>
constexpr auto make_just_t::operator()(T&& t) const {
return optional<typename std::decay<T>::type>(
return optional<detail::as_container_element_t<T>>(
static_cast<T&&>(t)
);
}
Expand Down
6 changes: 3 additions & 3 deletions include/boost/hana/pair.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Distributed under the Boost Software License, Version 1.0.

#include <boost/hana/basic_tuple.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/detail/decay.hpp>
#include <boost/hana/detail/as_container_element.hpp>
#include <boost/hana/detail/intrinsics.hpp>
#include <boost/hana/detail/operators/adl.hpp>
#include <boost/hana/detail/operators/comparable.hpp>
Expand Down Expand Up @@ -122,8 +122,8 @@ BOOST_HANA_NAMESPACE_BEGIN
struct make_impl<pair_tag> {
template <typename F, typename S>
static constexpr pair<
typename detail::decay<F>::type,
typename detail::decay<S>::type
detail::as_container_element_t<F>,
detail::as_container_element_t<S>
> apply(F&& f, S&& s) {
return {static_cast<F&&>(f), static_cast<S&&>(s)};
}
Expand Down
6 changes: 3 additions & 3 deletions include/boost/hana/tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Distributed under the Boost Software License, Version 1.0.
#include <boost/hana/basic_tuple.hpp>
#include <boost/hana/bool.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/detail/decay.hpp>
#include <boost/hana/detail/as_container_element.hpp>
#include <boost/hana/detail/fast_and.hpp>
#include <boost/hana/detail/index_if.hpp>
#include <boost/hana/detail/intrinsics.hpp>
Expand Down Expand Up @@ -277,8 +277,8 @@ BOOST_HANA_NAMESPACE_BEGIN
template <>
struct make_impl<tuple_tag> {
template <typename ...Xs>
static constexpr
tuple<typename detail::decay<Xs>::type...> apply(Xs&& ...xs)
static constexpr tuple<detail::as_container_element_t<Xs>...>
apply(Xs&& ...xs)
{ return {static_cast<Xs&&>(xs)...}; }
};

Expand Down
4 changes: 3 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/_support)
##############################################################################
if (NOT Boost_FOUND)
list(APPEND EXCLUDED_UNIT_TESTS
"detail/as_container_element/boost_ref.cpp"
"experimental/printable/*.cpp"
"ext/boost/*.cpp"
"experimental/printable/*.cpp")
)

list(APPEND EXCLUDED_PUBLIC_HEADERS
"${Boost.Hana_SOURCE_DIR}/include/boost/hana/ext/boost/*.hpp"
Expand Down
32 changes: 32 additions & 0 deletions test/detail/as_container_element/boost_ref.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#include <boost/hana/detail/as_container_element.hpp>

#include <boost/ref.hpp>

#include <type_traits>
namespace hana = boost::hana;
using hana::detail::as_container_element_t;


struct T { };

static_assert(std::is_same<T&, as_container_element_t<boost::reference_wrapper<T>>>{}, "");
static_assert(std::is_same<T&, as_container_element_t<boost::reference_wrapper<T> const>>{}, "");
static_assert(std::is_same<T&, as_container_element_t<boost::reference_wrapper<T>&>>{}, "");
static_assert(std::is_same<T&, as_container_element_t<boost::reference_wrapper<T> const&>>{}, "");
static_assert(std::is_same<T&, as_container_element_t<boost::reference_wrapper<T>&&>>{}, "");
static_assert(std::is_same<T&, as_container_element_t<boost::reference_wrapper<T> const&&>>{}, "");

static_assert(std::is_same<T const&, as_container_element_t<boost::reference_wrapper<T const>>>{}, "");
static_assert(std::is_same<T const&, as_container_element_t<boost::reference_wrapper<T const> const>>{}, "");
static_assert(std::is_same<T const&, as_container_element_t<boost::reference_wrapper<T const>&>>{}, "");
static_assert(std::is_same<T const&, as_container_element_t<boost::reference_wrapper<T const> const&>>{}, "");
static_assert(std::is_same<T const&, as_container_element_t<boost::reference_wrapper<T const>&&>>{}, "");
static_assert(std::is_same<T const&, as_container_element_t<boost::reference_wrapper<T const> const&&>>{}, "");

int main() { }
Loading