Skip to content

Commit 45ac9de

Browse files
committed
Fix missed opportunity for EBO in nested empty basic_tuples
Also, add tests to make sure the same does not happen in hana::pair.
1 parent ae2e966 commit 45ac9de

File tree

4 files changed

+127
-21
lines changed

4 files changed

+127
-21
lines changed

include/boost/hana/basic_tuple.hpp

+70-13
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Distributed under the Boost Software License, Version 1.0.
1515
#include <boost/hana/config.hpp>
1616
#include <boost/hana/detail/decay.hpp>
1717
#include <boost/hana/detail/ebo.hpp>
18+
#include <boost/hana/detail/type_at.hpp>
1819
#include <boost/hana/fwd/at.hpp>
1920
#include <boost/hana/fwd/bool.hpp>
2021
#include <boost/hana/fwd/concept/sequence.hpp>
@@ -72,7 +73,7 @@ BOOST_HANA_NAMESPACE_BEGIN
7273
//////////////////////////////////////////////////////////////////////////
7374
//! @cond
7475
template <typename ...Xn>
75-
struct basic_tuple final
76+
struct basic_tuple
7677
: detail::basic_tuple_impl<std::make_index_sequence<sizeof...(Xn)>, Xn...>
7778
{
7879
using Base = detail::basic_tuple_impl<std::make_index_sequence<sizeof...(Xn)>, Xn...>;
@@ -176,28 +177,75 @@ BOOST_HANA_NAMESPACE_BEGIN
176177
//////////////////////////////////////////////////////////////////////////
177178
template <>
178179
struct at_impl<basic_tuple_tag> {
179-
template <typename Xs, typename N>
180-
static constexpr decltype(auto) apply(Xs&& xs, N const&) {
180+
template <typename ...T, typename N>
181+
static constexpr decltype(auto) apply(hana::basic_tuple<T...>& xs, N const&) {
182+
constexpr std::size_t index = N::value;
183+
using Nth = typename detail::type_at<index, T...>::type;
184+
return detail::ebo_get<detail::bti<index>>(
185+
static_cast<detail::ebo<detail::bti<index>, Nth>&>(xs)
186+
);
187+
}
188+
189+
template <typename ...T, typename N>
190+
static constexpr decltype(auto) apply(hana::basic_tuple<T...>&& xs, N const&) {
181191
constexpr std::size_t index = N::value;
182-
return detail::ebo_get<detail::bti<index>>(static_cast<Xs&&>(xs));
192+
using Nth = typename detail::type_at<index, T...>::type;
193+
return detail::ebo_get<detail::bti<index>>(
194+
static_cast<detail::ebo<detail::bti<index>, Nth>&&>(xs)
195+
);
196+
}
197+
198+
template <typename ...T, typename N>
199+
static constexpr decltype(auto) apply(hana::basic_tuple<T...> const& xs, N const&) {
200+
constexpr std::size_t index = N::value;
201+
using Nth = typename detail::type_at<index, T...>::type;
202+
return detail::ebo_get<detail::bti<index>>(
203+
static_cast<detail::ebo<detail::bti<index>, Nth> const&>(xs)
204+
);
183205
}
184206
};
185207

186208
template <>
187209
struct drop_front_impl<basic_tuple_tag> {
188-
template <std::size_t N, typename Xs, std::size_t ...i>
189-
static constexpr auto drop_front_helper(Xs&& xs, std::index_sequence<i...>) {
210+
template <std::size_t N, typename ...Xs, std::size_t ...i>
211+
static constexpr auto
212+
drop_front_helper(hana::basic_tuple<Xs...>&& xs, std::index_sequence<i...>) {
190213
return hana::make_basic_tuple(
191-
detail::ebo_get<detail::bti<i+N>>(static_cast<Xs&&>(xs))...
214+
detail::ebo_get<detail::bti<i+N>>(
215+
static_cast<detail::ebo<
216+
detail::bti<i+N>, typename detail::type_at<i+N, Xs...>::type
217+
>&&>(xs)
218+
)...
219+
);
220+
}
221+
template <std::size_t N, typename ...Xs, std::size_t ...i>
222+
static constexpr auto
223+
drop_front_helper(hana::basic_tuple<Xs...>& xs, std::index_sequence<i...>) {
224+
return hana::make_basic_tuple(
225+
detail::ebo_get<detail::bti<i+N>>(
226+
static_cast<detail::ebo<
227+
detail::bti<i+N>, typename detail::type_at<i+N, Xs...>::type
228+
>&>(xs)
229+
)...
230+
);
231+
}
232+
template <std::size_t N, typename ...Xs, std::size_t ...i>
233+
static constexpr auto
234+
drop_front_helper(hana::basic_tuple<Xs...> const& xs, std::index_sequence<i...>) {
235+
return hana::make_basic_tuple(
236+
detail::ebo_get<detail::bti<i+N>>(
237+
static_cast<detail::ebo<
238+
detail::bti<i+N>, typename detail::type_at<i+N, Xs...>::type
239+
> const&>(xs)
240+
)...
192241
);
193242
}
194243

195244
template <typename Xs, typename N>
196245
static constexpr auto apply(Xs&& xs, N const&) {
197246
constexpr std::size_t len = detail::decay<Xs>::type::size_;
198-
return drop_front_helper<N::value>(static_cast<Xs&&>(xs), std::make_index_sequence<
199-
N::value < len ? len - N::value : 0
200-
>{});
247+
using Indices = std::make_index_sequence<N::value < len ? len - N::value : 0>;
248+
return drop_front_helper<N::value>(static_cast<Xs&&>(xs), Indices{});
201249
}
202250
};
203251

@@ -212,17 +260,26 @@ BOOST_HANA_NAMESPACE_BEGIN
212260
// compile-time optimizations (to reduce the # of function instantiations)
213261
template <std::size_t n, typename ...Xs>
214262
constexpr decltype(auto) at_c(basic_tuple<Xs...> const& xs) {
215-
return detail::ebo_get<detail::bti<n>>(xs);
263+
using Nth = typename detail::type_at<n, Xs...>::type;
264+
return detail::ebo_get<detail::bti<n>>(
265+
static_cast<detail::ebo<detail::bti<n>, Nth> const&>(xs)
266+
);
216267
}
217268

218269
template <std::size_t n, typename ...Xs>
219270
constexpr decltype(auto) at_c(basic_tuple<Xs...>& xs) {
220-
return detail::ebo_get<detail::bti<n>>(xs);
271+
using Nth = typename detail::type_at<n, Xs...>::type;
272+
return detail::ebo_get<detail::bti<n>>(
273+
static_cast<detail::ebo<detail::bti<n>, Nth>&>(xs)
274+
);
221275
}
222276

223277
template <std::size_t n, typename ...Xs>
224278
constexpr decltype(auto) at_c(basic_tuple<Xs...>&& xs) {
225-
return detail::ebo_get<detail::bti<n>>(static_cast<basic_tuple<Xs...>&&>(xs));
279+
using Nth = typename detail::type_at<n, Xs...>::type;
280+
return detail::ebo_get<detail::bti<n>>(
281+
static_cast<detail::ebo<detail::bti<n>, Nth>&&>(xs)
282+
);
226283
}
227284

228285
//////////////////////////////////////////////////////////////////////////

test/basic_tuple/drop_front.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright Louis Dionne 2013-2017
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
4+
5+
#include <boost/hana/basic_tuple.hpp>
6+
#include <boost/hana/drop_front.hpp>
7+
#include <boost/hana/integral_constant.hpp>
8+
namespace hana = boost::hana;
9+
10+
11+
template <int i>
12+
struct x { };
13+
14+
int main() {
15+
// Make sure drop_front works with nested tuples
16+
hana::basic_tuple<x<0>, hana::basic_tuple<x<1>, x<2>>> tuple;
17+
auto all = hana::drop_front(tuple, hana::size_c<0>); (void)all;
18+
}

test/basic_tuple/empty_storage.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright Louis Dionne 2013-2017
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
4+
5+
#include <boost/hana/basic_tuple.hpp>
6+
7+
#include <type_traits>
8+
namespace hana = boost::hana;
9+
10+
11+
struct empty1 { };
12+
struct empty2 { };
13+
struct empty3 { };
14+
15+
16+
// Make sure the storage of a basic_tuple is compressed.
17+
static_assert(sizeof(hana::basic_tuple<empty1, int>) == sizeof(int), "");
18+
static_assert(sizeof(hana::basic_tuple<int, empty1>) == sizeof(int), "");
19+
20+
// Also make sure that a basic_tuple with only empty members is empty. This is
21+
// important to ensure, for example, that a tuple of tuples of empty objects
22+
// will get the EBO. We also test the nested case to be sure.
23+
static_assert(std::is_empty<hana::basic_tuple<empty1, empty2>>{}, "");
24+
static_assert(std::is_empty<hana::basic_tuple<empty1, hana::basic_tuple<empty2, empty3>>>{}, "");
25+
26+
27+
int main() { }

test/pair/empty_storage.cpp

+12-8
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@
88
namespace hana = boost::hana;
99

1010

11-
// Make sure the storage of a pair is compressed
12-
struct empty { };
13-
static_assert(sizeof(hana::pair<empty, int>) == sizeof(int), "");
14-
static_assert(sizeof(hana::pair<int, empty>) == sizeof(int), "");
15-
16-
// Also make sure that a pair with only empty members is empty too. This is
17-
// important to ensure, for example, that a tuple of pairs of empty objects
18-
// will get the EBO.
1911
struct empty1 { };
2012
struct empty2 { };
13+
struct empty3 { };
14+
15+
16+
// Make sure the storage of a pair is compressed.
17+
static_assert(sizeof(hana::pair<empty1, int>) == sizeof(int), "");
18+
static_assert(sizeof(hana::pair<int, empty1>) == sizeof(int), "");
19+
20+
// Also make sure that a pair with only empty members is empty. This is
21+
// important to ensure, for example, that a tuple of pairs of empty
22+
// objects will get the EBO. We also test the nested case.
2123
static_assert(std::is_empty<hana::pair<empty1, empty2>>{}, "");
24+
static_assert(std::is_empty<hana::pair<empty1, hana::pair<empty2, empty3>>>{}, "");
25+
2226

2327
int main() { }

0 commit comments

Comments
 (0)