1// -*-c++-*-
2// vim: set ft=cpp:
3
4/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
5   file Copyright.txt or https://cmake.org/licensing for details.  */
6#pragma once
7
8#include <algorithm>
9#include <iterator>
10#include <memory>
11#include <utility>
12
13#include <cm/type_traits>
14#include <cmext/iterator>
15#include <cmext/type_traits>
16
17#if defined(__SUNPRO_CC) && defined(__sparc)
18#  include <list>
19#  include <vector>
20#endif
21
22namespace cm {
23
24#if defined(__SUNPRO_CC) && defined(__sparc)
25// Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile
26// templates with constraints.
27// So, on this platform, use only simple templates.
28#  define APPEND_TWO(C1, C2)                                                  \
29    template <typename T, typename U>                                         \
30    void append(C1<std::unique_ptr<T>>& v, C2<std::unique_ptr<U>>&& r)        \
31    {                                                                         \
32      std::transform(                                                         \
33        r.begin(), r.end(), std::back_inserter(v),                            \
34        [](std::unique_ptr<U>& item) { return std::move(item); });            \
35      r.clear();                                                              \
36    }                                                                         \
37                                                                              \
38    template <typename T, typename U>                                         \
39    void append(C1<T*>& v, C2<std::unique_ptr<U>> const& r)                   \
40    {                                                                         \
41      std::transform(                                                         \
42        r.begin(), r.end(), std::back_inserter(v),                            \
43        [](const std::unique_ptr<U>& item) { return item.get(); });           \
44    }
45
46#  define APPEND_ONE(C)                                                       \
47    template <typename T, typename InputIt,                                   \
48              cm::enable_if_t<cm::is_input_iterator<InputIt>::value, int> =   \
49                0>                                                            \
50    void append(C<T>& v, InputIt first, InputIt last)                         \
51    {                                                                         \
52      v.insert(v.end(), first, last);                                         \
53    }                                                                         \
54                                                                              \
55    template <typename T, typename Range,                                     \
56              cm::enable_if_t<cm::is_input_range<Range>::value, int> = 0>     \
57    void append(C<T>& v, Range const& r)                                      \
58    {                                                                         \
59      v.insert(v.end(), r.begin(), r.end());                                  \
60    }
61
62#  define APPEND(C)                                                           \
63    APPEND_TWO(C, C)                                                          \
64    APPEND_ONE(C)
65
66#  define APPEND_MIX(C1, C2)                                                  \
67    APPEND_TWO(C1, C2)                                                        \
68    APPEND_TWO(C2, C1)
69
70// For now, manage only support for std::vector and std::list.
71// Other sequential container support can be added if needed.
72APPEND(std::vector)
73APPEND(std::list)
74APPEND_MIX(std::vector, std::list)
75
76#  undef APPEND
77#  undef APPEND_MIX
78#  undef APPEND_TWO
79#  undef APPEND_ONE
80
81#else
82
83template <
84  typename Container1, typename Container2,
85  cm::enable_if_t<
86    cm::is_sequence_container<Container1>::value &&
87      cm::is_unique_ptr<typename Container1::value_type>::value &&
88      cm::is_unique_ptr<typename Container2::value_type>::value &&
89      std::is_convertible<typename Container2::value_type::pointer,
90                          typename Container1::value_type::pointer>::value,
91    int> = 0>
92void append(Container1& v, Container2&& r)
93{
94  std::transform(
95    r.begin(), r.end(), std::back_inserter(v),
96    [](typename Container2::value_type& item) { return std::move(item); });
97  r.clear();
98}
99
100template <typename Container1, typename Container2,
101          cm::enable_if_t<
102            cm::is_sequence_container<Container1>::value &&
103              std::is_pointer<typename Container1::value_type>::value &&
104              cm::is_unique_ptr<typename Container2::value_type>::value &&
105              std::is_convertible<typename Container2::value_type::pointer,
106                                  typename Container1::value_type>::value,
107            int> = 0>
108#  if defined(__SUNPRO_CC)
109void append(Container1& v, Container2 const& r, detail::overload_selector<0>)
110#  else
111void append(Container1& v, Container2 const& r)
112#  endif
113{
114  std::transform(
115    r.begin(), r.end(), std::back_inserter(v),
116    [](const typename Container2::value_type& item) { return item.get(); });
117}
118
119template <
120  typename Container, typename InputIt,
121  cm::enable_if_t<
122    cm::is_sequence_container<Container>::value &&
123      cm::is_input_iterator<InputIt>::value &&
124      std::is_convertible<typename std::iterator_traits<InputIt>::value_type,
125                          typename Container::value_type>::value,
126    int> = 0>
127void append(Container& v, InputIt first, InputIt last)
128{
129  v.insert(v.end(), first, last);
130}
131
132template <typename Container, typename Range,
133          cm::enable_if_t<
134            cm::is_sequence_container<Container>::value &&
135              cm::is_input_range<Range>::value &&
136              !cm::is_unique_ptr<typename Container::value_type>::value &&
137              !cm::is_unique_ptr<typename Range::value_type>::value &&
138              std::is_convertible<typename Range::value_type,
139                                  typename Container::value_type>::value,
140            int> = 0>
141#  if defined(__SUNPRO_CC)
142void append(Container& v, Range const& r, detail::overload_selector<1>)
143#  else
144void append(Container& v, Range const& r)
145#  endif
146{
147  v.insert(v.end(), r.begin(), r.end());
148}
149
150#  if defined(__SUNPRO_CC)
151template <typename T, typename U>
152void append(T& v, U const& r)
153{
154  cm::append(v, r, detail::overload_selector<1>{});
155}
156#  endif
157#endif
158
159#if defined(__SUNPRO_CC)
160template <typename Iterator, typename Key>
161auto contains(Iterator first, Iterator last, Key const& key,
162              detail::overload_selector<1>) -> decltype(first->first == key)
163#else
164template <typename Iterator, typename Key,
165          cm::enable_if_t<
166            cm::is_input_iterator<Iterator>::value &&
167              std::is_convertible<Key,
168                                  typename std::iterator_traits<
169                                    Iterator>::value_type::first_type>::value,
170            int> = 0>
171bool contains(Iterator first, Iterator last, Key const& key)
172#endif
173{
174  return std::find_if(
175           first, last,
176           [&key](
177             typename std::iterator_traits<Iterator>::value_type const& item) {
178             return item.first == key;
179           }) != last;
180}
181
182#if defined(__SUNPRO_CC)
183template <typename Iterator, typename Key>
184bool contains(Iterator first, Iterator last, Key const& key,
185              detail::overload_selector<0>)
186#else
187template <
188  typename Iterator, typename Key,
189  cm::enable_if_t<
190    cm::is_input_iterator<Iterator>::value &&
191      std::is_convertible<
192        Key, typename std::iterator_traits<Iterator>::value_type>::value,
193    int> = 0>
194bool contains(Iterator first, Iterator last, Key const& key)
195#endif
196{
197  return std::find(first, last, key) != last;
198}
199
200#if defined(__SUNPRO_CC)
201template <typename Iterator, typename Key>
202bool contains(Iterator first, Iterator last, Key const& key)
203{
204  return contains(first, last, key, detail::overload_selector<1>{});
205}
206#endif
207
208#if defined(__SUNPRO_CC)
209template <typename Range, typename Key>
210auto contains(Range const& range, Key const& key, detail::overload_selector<1>)
211  -> decltype(range.find(key) != range.end())
212#else
213template <
214  typename Range, typename Key,
215  cm::enable_if_t<cm::is_associative_container<Range>::value ||
216                    cm::is_unordered_associative_container<Range>::value,
217                  int> = 0>
218bool contains(Range const& range, Key const& key)
219#endif
220{
221  return range.find(key) != range.end();
222}
223
224#if defined(__SUNPRO_CC)
225template <typename Range, typename Key>
226bool contains(Range const& range, Key const& key, detail::overload_selector<0>)
227#else
228template <
229  typename Range, typename Key,
230  cm::enable_if_t<cm::is_input_range<Range>::value &&
231                    !(cm::is_associative_container<Range>::value ||
232                      cm::is_unordered_associative_container<Range>::value),
233                  int> = 0>
234bool contains(Range const& range, Key const& key)
235#endif
236{
237  return std::find(std::begin(range), std::end(range), key) != std::end(range);
238}
239
240#if defined(__SUNPRO_CC)
241template <typename Range, typename Key>
242bool contains(Range const& range, Key const& key)
243{
244  return contains(range, key, detail::overload_selector<1>{});
245}
246#endif
247
248} // namespace cm
249