1 #ifndef _GLIBMM_LISTHANDLE_H
2 #define _GLIBMM_LISTHANDLE_H
3
4 /* Copyright (C) 2002 The gtkmm Development Team
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <glibmmconfig.h>
21 #include <glibmm/containerhandle_shared.h>
22 #include <glib.h>
23
24 namespace Glib
25 {
26
27 namespace Container_Helpers
28 {
29
30 #ifndef DOXYGEN_SHOULD_SKIP_THIS
31
32 /* Create and fill a GList as efficient as possible.
33 * This requires bidirectional iterators.
34 */
35 template <class Bi, class Tr>
36 GList*
create_list(Bi pbegin,Bi pend,Tr)37 create_list(Bi pbegin, Bi pend, Tr)
38 {
39 GList* head = nullptr;
40
41 while (pend != pbegin)
42 {
43 // Use & to force a warning if the iterator returns a temporary object.
44 const void* const item = Tr::to_c_type(*&*--pend);
45 head = g_list_prepend(head, const_cast<void*>(item));
46 }
47
48 return head;
49 }
50
51 /* Create a GList from a 0-terminated input sequence.
52 * Build it in reverse order and reverse the whole list afterwards,
53 * because appending to the list would be horribly inefficient.
54 */
55 template <class For, class Tr>
56 GList*
create_list(For pbegin,Tr)57 create_list(For pbegin, Tr)
58 {
59 GList* head = nullptr;
60
61 while (*pbegin)
62 {
63 // Use & to force a warning if the iterator returns a temporary object.
64 const void* const item = Tr::to_c_type(*&*pbegin);
65 head = g_list_prepend(head, const_cast<void*>(item));
66 ++pbegin;
67 }
68
69 return g_list_reverse(head);
70 }
71
72 /* Convert from any container that supports bidirectional iterators.
73 */
74 template <class Tr, class Cont>
75 struct ListSourceTraits
76 {
get_dataListSourceTraits77 static GList* get_data(const Cont& cont)
78 {
79 return Glib::Container_Helpers::create_list(cont.begin(), cont.end(), Tr());
80 }
81
82 static const Glib::OwnershipType initial_ownership = Glib::OWNERSHIP_SHALLOW;
83 };
84
85 /* Convert from a 0-terminated array. The Cont
86 * argument must be a pointer to the first element.
87 */
88 template <class Tr, class Cont>
89 struct ListSourceTraits<Tr, Cont*>
90 {
91 static GList* get_data(const Cont* array)
92 {
93 return (array) ? Glib::Container_Helpers::create_list(array, Tr()) : nullptr;
94 }
95
96 static const Glib::OwnershipType initial_ownership = Glib::OWNERSHIP_SHALLOW;
97 };
98
99 template <class Tr, class Cont>
100 struct ListSourceTraits<Tr, const Cont*> : ListSourceTraits<Tr, Cont*>
101 {
102 };
103
104 /* Convert from a 0-terminated array. The Cont argument must be a pointer
105 * to the first element. For consistency, the array must be 0-terminated,
106 * even though the array size is known at compile time.
107 */
108 template <class Tr, class Cont, std::size_t N>
109 struct ListSourceTraits<Tr, Cont[N]>
110 {
111 static GList* get_data(const Cont* array)
112 {
113 return Glib::Container_Helpers::create_list(array, array + (N - 1), Tr());
114 }
115
116 static const Glib::OwnershipType initial_ownership = Glib::OWNERSHIP_SHALLOW;
117 };
118
119 template <class Tr, class Cont, std::size_t N>
120 struct ListSourceTraits<Tr, const Cont[N]> : ListSourceTraits<Tr, Cont[N]>
121 {
122 };
123
124 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
125
126 /**
127 * @ingroup ContHelpers
128 */
129 template <class Tr>
130 class ListHandleIterator
131 {
132 public:
133 using CppType = typename Tr::CppType;
134 using CType = typename Tr::CType;
135
136 using iterator_category = std::forward_iterator_tag;
137 using value_type = CppType;
138 using difference_type = std::ptrdiff_t;
139 using reference = value_type;
140 using pointer = void;
141
142 explicit inline ListHandleIterator(const GList* node);
143
144 inline value_type operator*() const;
145 inline ListHandleIterator<Tr>& operator++();
146 inline const ListHandleIterator<Tr> operator++(int);
147
148 inline bool operator==(const ListHandleIterator<Tr>& rhs) const;
149 inline bool operator!=(const ListHandleIterator<Tr>& rhs) const;
150
151 private:
152 const GList* node_;
153 };
154
155 } // namespace Container_Helpers
156
157 // TODO: Remove this when we can break glibmm API.
158 /** This is an intermediate type. When a method takes this, or returns this, you
159 * should use a standard C++ container of your choice, such as std::list or
160 * std::vector.
161 *
162 * However, this is not used in new API. We now prefer to just use std::vector,
163 * which is less flexibile, but makes the API clearer.
164 *
165 * @ingroup ContHandles
166 */
167 template <class T, class Tr = Glib::Container_Helpers::TypeTraits<T>>
168 class ListHandle
169 {
170 public:
171 using CppType = typename Tr::CppType;
172 using CType = typename Tr::CType;
173
174 using value_type = CppType;
175 using size_type = std::size_t;
176 using difference_type = std::ptrdiff_t;
177
178 using const_iterator = Glib::Container_Helpers::ListHandleIterator<Tr>;
179 using iterator = Glib::Container_Helpers::ListHandleIterator<Tr>;
180
181 template <class Cont>
182 inline ListHandle(const Cont& container);
183
184 // Take over ownership of an array created by GTK+ functions.
185 inline ListHandle(GList* glist, Glib::OwnershipType ownership);
186
187 // Copying clears the ownership flag of the source handle.
188 inline ListHandle(const ListHandle<T, Tr>& other);
189
190 ~ListHandle() noexcept;
191
192 inline const_iterator begin() const;
193 inline const_iterator end() const;
194
195 template <class U>
196 inline operator std::vector<U>() const;
197 template <class U>
198 inline operator std::deque<U>() const;
199 template <class U>
200 inline operator std::list<U>() const;
201
202 template <class Cont>
203 inline void assign_to(Cont& container) const;
204
205 template <class Out>
206 inline void copy(Out pdest) const;
207
208 inline GList* data() const;
209 inline std::size_t size() const;
210 inline bool empty() const;
211
212 private:
213 GList* plist_;
214 mutable Glib::OwnershipType ownership_;
215
216 // No copy assignment.
217 ListHandle<T, Tr>& operator=(const ListHandle<T, Tr>&);
218 };
219
220 /***************************************************************************/
221 /* Inline implementation */
222 /***************************************************************************/
223
224 #ifndef DOXYGEN_SHOULD_SKIP_THIS
225
226 namespace Container_Helpers
227 {
228
229 /**** Glib::Container_Helpers::ListHandleIterator<> ************************/
230
231 template <class Tr>
232 inline ListHandleIterator<Tr>::ListHandleIterator(const GList* node) : node_(node)
233 {
234 }
235
236 template <class Tr>
237 inline typename ListHandleIterator<Tr>::value_type ListHandleIterator<Tr>::operator*() const
238 {
239 return Tr::to_cpp_type(static_cast<typename Tr::CTypeNonConst>(node_->data));
240 }
241
242 template <class Tr>
243 inline ListHandleIterator<Tr>& ListHandleIterator<Tr>::operator++()
244 {
245 node_ = node_->next;
246 return *this;
247 }
248
249 template <class Tr>
250 inline const ListHandleIterator<Tr> ListHandleIterator<Tr>::operator++(int)
251 {
252 const ListHandleIterator<Tr> tmp(*this);
253 node_ = node_->next;
254 return tmp;
255 }
256
257 template <class Tr>
258 inline bool
259 ListHandleIterator<Tr>::operator==(const ListHandleIterator<Tr>& rhs) const
260 {
261 return (node_ == rhs.node_);
262 }
263
264 template <class Tr>
265 inline bool
266 ListHandleIterator<Tr>::operator!=(const ListHandleIterator<Tr>& rhs) const
267 {
268 return (node_ != rhs.node_);
269 }
270
271 } // namespace Container_Helpers
272
273 /**** Glib::ListHandle<> ***************************************************/
274
275 template <class T, class Tr>
276 template <class Cont>
277 inline ListHandle<T, Tr>::ListHandle(const Cont& container)
278 : plist_(Glib::Container_Helpers::ListSourceTraits<Tr, Cont>::get_data(container)),
279 ownership_(Glib::Container_Helpers::ListSourceTraits<Tr, Cont>::initial_ownership)
280 {
281 }
282
283 template <class T, class Tr>
284 inline ListHandle<T, Tr>::ListHandle(GList* glist, Glib::OwnershipType ownership)
285 : plist_(glist), ownership_(ownership)
286 {
287 }
288
289 template <class T, class Tr>
290 inline ListHandle<T, Tr>::ListHandle(const ListHandle<T, Tr>& other)
291 : plist_(other.plist_), ownership_(other.ownership_)
292 {
293 other.ownership_ = Glib::OWNERSHIP_NONE;
294 }
295
296 template <class T, class Tr>
297 ListHandle<T, Tr>::~ListHandle() noexcept
298 {
299 if (ownership_ != Glib::OWNERSHIP_NONE)
300 {
301 if (ownership_ != Glib::OWNERSHIP_SHALLOW)
302 {
303 // Deep ownership: release each container element.
304 for (GList* node = plist_; node != nullptr; node = node->next)
305 Tr::release_c_type(static_cast<typename Tr::CTypeNonConst>(node->data));
306 }
307 g_list_free(plist_);
308 }
309 }
310
311 template <class T, class Tr>
312 inline typename ListHandle<T, Tr>::const_iterator
313 ListHandle<T, Tr>::begin() const
314 {
315 return Glib::Container_Helpers::ListHandleIterator<Tr>(plist_);
316 }
317
318 template <class T, class Tr>
319 inline typename ListHandle<T, Tr>::const_iterator
320 ListHandle<T, Tr>::end() const
321 {
322 return Glib::Container_Helpers::ListHandleIterator<Tr>(nullptr);
323 }
324
325 template <class T, class Tr>
326 template <class U>
327 inline ListHandle<T, Tr>::operator std::vector<U>() const
328 {
329 #ifdef GLIBMM_HAVE_TEMPLATE_SEQUENCE_CTORS
330 return std::vector<U>(this->begin(), this->end());
331 #else
332 std::vector<U> temp;
333 temp.reserve(this->size());
334 Glib::Container_Helpers::fill_container(temp, this->begin(), this->end());
335 return temp;
336 #endif
337 }
338
339 template <class T, class Tr>
340 template <class U>
341 inline ListHandle<T, Tr>::operator std::deque<U>() const
342 {
343 #ifdef GLIBMM_HAVE_TEMPLATE_SEQUENCE_CTORS
344 return std::deque<U>(this->begin(), this->end());
345 #else
346 std::deque<U> temp;
347 Glib::Container_Helpers::fill_container(temp, this->begin(), this->end());
348 return temp;
349 #endif
350 }
351
352 template <class T, class Tr>
353 template <class U>
354 inline ListHandle<T, Tr>::operator std::list<U>() const
355 {
356 #ifdef GLIBMM_HAVE_TEMPLATE_SEQUENCE_CTORS
357 return std::list<U>(this->begin(), this->end());
358 #else
359 std::list<U> temp;
360 Glib::Container_Helpers::fill_container(temp, this->begin(), this->end());
361 return temp;
362 #endif
363 }
364
365 template <class T, class Tr>
366 template <class Cont>
367 inline void
368 ListHandle<T, Tr>::assign_to(Cont& container) const
369 {
370 #ifdef GLIBMM_HAVE_TEMPLATE_SEQUENCE_CTORS
371 container.assign(this->begin(), this->end());
372 #else
373 Cont temp;
374 Glib::Container_Helpers::fill_container(temp, this->begin(), this->end());
375 container.swap(temp);
376 #endif
377 }
378
379 template <class T, class Tr>
380 template <class Out>
381 inline void
382 ListHandle<T, Tr>::copy(Out pdest) const
383 {
384 std::copy(this->begin(), this->end(), pdest);
385 }
386
387 template <class T, class Tr>
388 inline GList*
389 ListHandle<T, Tr>::data() const
390 {
391 return plist_;
392 }
393
394 template <class T, class Tr>
395 inline std::size_t
396 ListHandle<T, Tr>::size() const
397 {
398 return g_list_length(plist_);
399 }
400
401 template <class T, class Tr>
402 inline bool
403 ListHandle<T, Tr>::empty() const
404 {
405 return (plist_ == nullptr);
406 }
407
408 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
409
410 } // namespace Glib
411
412 #endif /* _GLIBMM_LISTHANDLE_H */
413