3 Defines `boost::hana::map`.
5 @copyright Louis Dionne 2013-2017
6 Distributed under the Boost Software License, Version 1.0.
7 (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
10 #ifndef BOOST_HANA_MAP_HPP
11 #define BOOST_HANA_MAP_HPP
13 #include <boost/hana/fwd/map.hpp>
15 #include <boost/hana/all_of.hpp>
16 #include <boost/hana/basic_tuple.hpp>
17 #include <boost/hana/bool.hpp>
18 #include <boost/hana/concept/comparable.hpp>
19 #include <boost/hana/concept/constant.hpp>
20 #include <boost/hana/concept/product.hpp>
21 #include <boost/hana/config.hpp>
22 #include <boost/hana/contains.hpp>
23 #include <boost/hana/core/is_a.hpp>
24 #include <boost/hana/core/make.hpp>
25 #include <boost/hana/core/to.hpp>
26 #include <boost/hana/detail/decay.hpp>
27 #include <boost/hana/detail/fast_and.hpp>
28 #include <boost/hana/detail/has_duplicates.hpp>
29 #include <boost/hana/detail/hash_table.hpp>
30 #include <boost/hana/detail/intrinsics.hpp>
31 #include <boost/hana/detail/operators/adl.hpp>
32 #include <boost/hana/detail/operators/comparable.hpp>
33 #include <boost/hana/detail/operators/searchable.hpp>
34 #include <boost/hana/equal.hpp>
35 #include <boost/hana/find.hpp>
36 #include <boost/hana/first.hpp>
37 #include <boost/hana/fold_left.hpp>
38 #include <boost/hana/functional/demux.hpp>
39 #include <boost/hana/functional/on.hpp>
40 #include <boost/hana/functional/partial.hpp>
41 #include <boost/hana/fwd/any_of.hpp>
42 #include <boost/hana/fwd/at_key.hpp>
43 #include <boost/hana/fwd/difference.hpp>
44 #include <boost/hana/fwd/erase_key.hpp>
45 #include <boost/hana/fwd/intersection.hpp>
46 #include <boost/hana/fwd/is_subset.hpp>
47 #include <boost/hana/fwd/keys.hpp>
48 #include <boost/hana/fwd/union.hpp>
49 #include <boost/hana/insert.hpp>
50 #include <boost/hana/integral_constant.hpp>
51 #include <boost/hana/keys.hpp>
52 #include <boost/hana/length.hpp>
53 #include <boost/hana/optional.hpp>
54 #include <boost/hana/remove_if.hpp>
55 #include <boost/hana/second.hpp>
56 #include <boost/hana/unpack.hpp>
57 #include <boost/hana/value.hpp>
61 #include <type_traits>
65 BOOST_HANA_NAMESPACE_BEGIN
66 //////////////////////////////////////////////////////////////////////////
68 //////////////////////////////////////////////////////////////////////////
71 struct comparable_operators<map_tag> {
72 static constexpr bool value = true;
76 //////////////////////////////////////////////////////////////////////////
78 //////////////////////////////////////////////////////////////////////////
81 template <typename ...>
82 struct storage_is_default_constructible;
83 template <typename ...T>
84 struct storage_is_default_constructible<hana::basic_tuple<T...>> {
85 static constexpr bool value = detail::fast_and<
86 BOOST_HANA_TT_IS_CONSTRUCTIBLE(T)...
90 template <typename ...>
91 struct storage_is_copy_constructible;
92 template <typename ...T>
93 struct storage_is_copy_constructible<hana::basic_tuple<T...>> {
94 static constexpr bool value = detail::fast_and<
95 BOOST_HANA_TT_IS_CONSTRUCTIBLE(T, T const&)...
99 template <typename ...>
100 struct storage_is_move_constructible;
101 template <typename ...T>
102 struct storage_is_move_constructible<hana::basic_tuple<T...>> {
103 static constexpr bool value = detail::fast_and<
104 BOOST_HANA_TT_IS_CONSTRUCTIBLE(T, T&&)...
108 template <typename ...>
109 struct storage_is_copy_assignable;
110 template <typename ...T>
111 struct storage_is_copy_assignable<hana::basic_tuple<T...>> {
112 static constexpr bool value = detail::fast_and<
113 BOOST_HANA_TT_IS_ASSIGNABLE(T, T const&)...
117 template <typename ...>
118 struct storage_is_move_assignable;
119 template <typename ...T>
120 struct storage_is_move_assignable<hana::basic_tuple<T...>> {
121 static constexpr bool value = detail::fast_and<
122 BOOST_HANA_TT_IS_ASSIGNABLE(T, T&&)...
126 template <typename HashTable, typename Storage>
128 : detail::searchable_operators<map_impl<HashTable, Storage>>
129 , detail::operators::adl<map_impl<HashTable, Storage>>
131 using hash_table_type = HashTable;
132 using storage_type = Storage;
136 using hana_tag = map_tag;
138 template <typename ...P, typename = typename std::enable_if<
141 hana::basic_tuple<typename detail::decay<P>::type...>
144 explicit constexpr map_impl(P&& ...pairs)
145 : storage{static_cast<P&&>(pairs)...}
148 explicit constexpr map_impl(Storage&& xs)
149 : storage(static_cast<Storage&&>(xs))
152 template <typename ...Dummy, typename = typename std::enable_if<
153 detail::storage_is_default_constructible<Storage, Dummy...>::value
159 template <typename ...Dummy, typename = typename std::enable_if<
160 detail::storage_is_copy_constructible<Storage, Dummy...>::value
162 constexpr map_impl(map_impl const& other)
163 : storage(other.storage)
166 template <typename ...Dummy, typename = typename std::enable_if<
167 detail::storage_is_move_constructible<Storage, Dummy...>::value
169 constexpr map_impl(map_impl&& other)
170 : storage(static_cast<Storage&&>(other.storage))
173 template <typename ...Dummy, typename = typename std::enable_if<
174 detail::storage_is_move_assignable<Storage, Dummy...>::value
176 constexpr map_impl& operator=(map_impl&& other) {
177 storage = static_cast<Storage&&>(other.storage);
181 template <typename ...Dummy, typename = typename std::enable_if<
182 detail::storage_is_copy_assignable<Storage, Dummy...>::value
184 constexpr map_impl& operator=(map_impl const& other) {
185 storage = other.storage;
189 // Prevent the compiler from defining the default copy and move
190 // constructors, which interfere with the SFINAE above.
191 ~map_impl() = default;
195 template <typename Storage>
197 template <std::size_t i>
198 using apply = decltype(hana::first(hana::at_c<i>(std::declval<Storage>())));
201 template <typename ...Pairs>
202 struct make_map_type {
203 using Storage = hana::basic_tuple<Pairs...>;
204 using HashTable = typename detail::make_hash_table<
205 detail::KeyAtIndex<Storage>::template apply, sizeof...(Pairs)
207 using type = detail::map_impl<HashTable, Storage>;
211 //////////////////////////////////////////////////////////////////////////
213 //////////////////////////////////////////////////////////////////////////
215 struct make_impl<map_tag> {
216 template <typename ...Pairs>
217 static constexpr auto apply(Pairs&& ...pairs) {
218 #if defined(BOOST_HANA_CONFIG_ENABLE_DEBUG_MODE)
219 static_assert(detail::fast_and<hana::Product<Pairs>::value...>::value,
220 "hana::make_map(pairs...) requires all the 'pairs' to be Products");
222 static_assert(detail::fast_and<
223 hana::Comparable<decltype(hana::first(pairs))>::value...
225 "hana::make_map(pairs...) requires all the keys to be Comparable");
227 static_assert(detail::fast_and<
229 decltype(hana::equal(hana::first(pairs), hana::first(pairs)))
232 "hana::make_map(pairs...) requires all the keys to be "
233 "Comparable at compile-time");
236 //! This can be implemented more efficiently by doing the check
237 //! inside each bucket instead.
238 static_assert(!detail::has_duplicates<decltype(hana::first(pairs))...>::value,
239 "hana::make_map({keys, values}...) requires all the keys to be unique");
241 static_assert(!detail::has_duplicates<decltype(hana::hash(hana::first(pairs)))...>::value,
242 "hana::make_map({keys, values}...) requires all the keys to have different hashes");
245 using Map = typename detail::make_map_type<typename detail::decay<Pairs>::type...>::type;
246 return Map{hana::make_basic_tuple(static_cast<Pairs&&>(pairs)...)};
250 //////////////////////////////////////////////////////////////////////////
252 //////////////////////////////////////////////////////////////////////////
254 struct keys_impl<map_tag> {
255 template <typename Map>
256 static constexpr decltype(auto) apply(Map&& map) {
257 return hana::transform(static_cast<Map&&>(map).storage, hana::first);
261 //////////////////////////////////////////////////////////////////////////
263 //////////////////////////////////////////////////////////////////////////
265 template <typename Map>
266 constexpr decltype(auto) values_t::operator()(Map&& map) const {
267 return hana::transform(static_cast<Map&&>(map).storage, hana::second);
271 //////////////////////////////////////////////////////////////////////////
273 //////////////////////////////////////////////////////////////////////////
275 struct insert_impl<map_tag> {
276 template <typename Map, typename Pair>
277 static constexpr auto helper(Map&& map, Pair&& pair, ...) {
278 using RawMap = typename std::remove_reference<Map>::type;
279 using HashTable = typename RawMap::hash_table_type;
280 using NewHashTable = typename detail::bucket_insert<
282 decltype(hana::first(pair)),
283 decltype(hana::length(map.storage))::value
286 using NewStorage = decltype(
287 hana::append(static_cast<Map&&>(map).storage, static_cast<Pair&&>(pair))
289 return detail::map_impl<NewHashTable, NewStorage>(
290 hana::append(static_cast<Map&&>(map).storage, static_cast<Pair&&>(pair))
294 template <typename Map, typename Pair, std::size_t i>
295 static constexpr auto
296 helper(Map&& map, Pair&&,
297 hana::optional<std::integral_constant<std::size_t, i>>)
299 return static_cast<Map&&>(map);
303 //! Here, we insert only if the key is not already in the map.
304 //! This should be handled by `bucket_insert`, and that would also
305 //! be more efficient.
306 template <typename Map, typename Pair>
307 static constexpr auto apply(Map&& map, Pair&& pair) {
308 using RawMap = typename std::remove_reference<Map>::type;
309 using Storage = typename RawMap::storage_type;
310 using HashTable = typename RawMap::hash_table_type;
311 using Key = decltype(hana::first(pair));
312 using MaybeIndex = typename detail::find_index<
313 HashTable, Key, detail::KeyAtIndex<Storage>::template apply
315 return helper(static_cast<Map&&>(map), static_cast<Pair&&>(pair), MaybeIndex{});
319 //////////////////////////////////////////////////////////////////////////
321 //////////////////////////////////////////////////////////////////////////
323 struct erase_key_impl<map_tag> {
325 //! We could implement some kind of `bucket_erase` metafunction
326 //! that would be much more efficient than this.
327 template <typename Map, typename Key>
328 static constexpr auto
329 erase_key_helper(Map&& map, Key const&, hana::false_) {
330 return static_cast<Map&&>(map);
333 template <typename Map, typename Key>
334 static constexpr auto
335 erase_key_helper(Map&& map, Key const& key, hana::true_) {
337 hana::remove_if(static_cast<Map&&>(map).storage,
338 hana::on(hana::equal.to(key), hana::first)),
343 template <typename Map, typename Key>
344 static constexpr auto apply_impl(Map&& map, Key const& key, hana::false_) {
345 return erase_key_helper(static_cast<Map&&>(map), key,
346 hana::contains(map, key));
349 template <typename Map, typename Key>
350 static constexpr auto apply_impl(Map&& map, Key const&, hana::true_) {
351 return static_cast<Map&&>(map);
354 template <typename Map, typename Key>
355 static constexpr auto apply(Map&& map, Key const& key) {
356 constexpr bool is_empty = decltype(hana::length(map))::value == 0;
357 return apply_impl(static_cast<Map&&>(map), key, hana::bool_<is_empty>{});
361 //////////////////////////////////////////////////////////////////////////
363 //////////////////////////////////////////////////////////////////////////
365 struct equal_impl<map_tag, map_tag> {
366 template <typename M1, typename M2>
367 static constexpr auto equal_helper(M1 const&, M2 const&, hana::false_) {
368 return hana::false_c;
371 template <typename M1, typename M2>
372 static constexpr auto equal_helper(M1 const& m1, M2 const& m2, hana::true_) {
373 return hana::all_of(hana::keys(m1), hana::demux(equal)(
374 hana::partial(hana::find, m1),
375 hana::partial(hana::find, m2)
379 template <typename M1, typename M2>
380 static constexpr auto apply(M1 const& m1, M2 const& m2) {
381 return equal_impl::equal_helper(m1, m2, hana::bool_c<
382 decltype(hana::length(m1.storage))::value ==
383 decltype(hana::length(m2.storage))::value
388 //////////////////////////////////////////////////////////////////////////
390 //////////////////////////////////////////////////////////////////////////
392 struct find_impl<map_tag> {
393 template <typename Map>
394 static constexpr auto find_helper(Map&&, ...) {
395 return hana::nothing;
398 template <typename Map, std::size_t i>
399 static constexpr auto
400 find_helper(Map&& map, hana::optional<std::integral_constant<std::size_t, i>>) {
401 return hana::just(hana::second(hana::at_c<i>(static_cast<Map&&>(map).storage)));
404 template <typename Map, typename Key>
405 static constexpr auto apply(Map&& map, Key const&) {
406 using RawMap = typename std::remove_reference<Map>::type;
407 using Storage = typename RawMap::storage_type;
408 using HashTable = typename RawMap::hash_table_type;
409 using MaybeIndex = typename detail::find_index<
410 HashTable, Key, detail::KeyAtIndex<Storage>::template apply
412 return find_helper(static_cast<Map&&>(map), MaybeIndex{});
417 struct find_if_impl<map_tag> {
418 template <typename M, typename Pred>
419 static constexpr auto apply(M&& map, Pred&& pred) {
420 return hana::transform(
421 hana::find_if(static_cast<M&&>(map).storage,
422 hana::compose(static_cast<Pred&&>(pred), hana::first)),
429 struct contains_impl<map_tag> {
430 template <typename Map, typename Key>
431 static constexpr auto apply(Map const&, Key const&) {
432 using RawMap = typename std::remove_reference<Map>::type;
433 using HashTable = typename RawMap::hash_table_type;
434 using Storage = typename RawMap::storage_type;
435 using MaybeIndex = typename detail::find_index<
436 HashTable, Key, detail::KeyAtIndex<Storage>::template apply
438 return hana::bool_<!decltype(hana::is_nothing(MaybeIndex{}))::value>{};
443 struct any_of_impl<map_tag> {
444 template <typename M, typename Pred>
445 static constexpr auto apply(M const& map, Pred const& pred)
446 { return hana::any_of(hana::keys(map), pred); }
450 struct is_subset_impl<map_tag, map_tag> {
451 template <typename Ys>
452 struct all_contained {
454 template <typename ...X>
455 constexpr auto operator()(X const& ...x) const {
456 return hana::bool_c<detail::fast_and<
457 hana::value<decltype(hana::contains(ys, x))>()...
462 template <typename Xs, typename Ys>
463 static constexpr auto apply(Xs const& xs, Ys const& ys) {
464 auto ys_keys = hana::keys(ys);
465 return hana::unpack(hana::keys(xs), all_contained<decltype(ys_keys)>{ys_keys});
470 struct at_key_impl<map_tag> {
471 template <typename Map, typename Key>
472 static constexpr decltype(auto) apply(Map&& map, Key const&) {
473 using RawMap = typename std::remove_reference<Map>::type;
474 using HashTable = typename RawMap::hash_table_type;
475 using Storage = typename RawMap::storage_type;
476 using MaybeIndex = typename detail::find_index<
477 HashTable, Key, detail::KeyAtIndex<Storage>::template apply
479 static_assert(!decltype(hana::is_nothing(MaybeIndex{}))::value,
480 "hana::at_key(map, key) requires the 'key' to be present in the 'map'");
481 constexpr std::size_t index = decltype(*MaybeIndex{}){}();
482 return hana::second(hana::at_c<index>(static_cast<Map&&>(map).storage));
486 //////////////////////////////////////////////////////////////////////////
488 //////////////////////////////////////////////////////////////////////////
490 struct union_impl<map_tag> {
491 template <typename Xs, typename Ys>
492 static constexpr auto apply(Xs&& xs, Ys&& ys) {
493 return hana::fold_left(static_cast<Xs&&>(xs), static_cast<Ys&&>(ys),
498 //////////////////////////////////////////////////////////////////////////
500 //////////////////////////////////////////////////////////////////////////
502 template <typename Ys>
503 struct map_insert_if_contains {
506 // Second template param will be pair
507 // Get its key and check if it exists, if it does, insert key, value pair.
508 template <typename Result, typename Pair>
509 static constexpr auto helper(Result&& result, Pair&& pair, hana::true_) {
510 return hana::insert(static_cast<Result&&>(result), static_cast<Pair&&>(pair));
513 template <typename Result, typename Pair>
514 static constexpr auto helper(Result&& result, Pair&&, hana::false_) {
515 return static_cast<Result&&>(result);
518 template <typename Result, typename Pair>
519 constexpr auto operator()(Result&& result, Pair&& pair) const {
520 constexpr bool keep = hana::value<decltype(hana::contains(ys, hana::first(pair)))>();
521 return map_insert_if_contains::helper(static_cast<Result&&>(result),
522 static_cast<Pair&&>(pair),
529 struct intersection_impl<map_tag> {
530 template <typename Xs, typename Ys>
531 static constexpr auto apply(Xs&& xs, Ys const& ys) {
532 return hana::fold_left(static_cast<Xs&&>(xs), hana::make_map(),
533 detail::map_insert_if_contains<Ys>{ys});
537 //////////////////////////////////////////////////////////////////////////
539 //////////////////////////////////////////////////////////////////////////
541 struct difference_impl<map_tag> {
542 template <typename Xs, typename Ys>
543 static constexpr auto apply(Xs&& xs, Ys&& ys) {
544 return hana::fold_left(
545 hana::keys(static_cast<Ys&&>(ys)),
546 static_cast<Xs&&>(xs),
551 //////////////////////////////////////////////////////////////////////////
553 //////////////////////////////////////////////////////////////////////////
555 struct unpack_impl<map_tag> {
556 template <typename M, typename F>
557 static constexpr decltype(auto) apply(M&& map, F&& f) {
558 return hana::unpack(static_cast<M&&>(map).storage,
559 static_cast<F&&>(f));
563 //////////////////////////////////////////////////////////////////////////
564 // Construction from a Foldable
565 //////////////////////////////////////////////////////////////////////////
566 template <typename F>
567 struct to_impl<map_tag, F, when<hana::Foldable<F>::value>> {
568 template <typename Xs>
569 static constexpr decltype(auto) apply(Xs&& xs) {
570 return hana::fold_left(
571 static_cast<Xs&&>(xs), hana::make_map(), hana::insert
575 BOOST_HANA_NAMESPACE_END
577 #endif // !BOOST_HANA_MAP_HPP