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