1 #ifndef _GLIBMM_SLISTHANDLE_H
2 #define _GLIBMM_SLISTHANDLE_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 GSList as efficient as possible.
33  * This requires bidirectional iterators.
34  */
35 template <class Bi, class Tr>
36 GSList*
create_slist(Bi pbegin,Bi pend,Tr)37 create_slist(Bi pbegin, Bi pend, Tr)
38 {
39   GSList* 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_slist_prepend(head, const_cast<void*>(item));
46   }
47 
48   return head;
49 }
50 
51 /* Create a GSList 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 GSList*
create_slist(For pbegin,Tr)57 create_slist(For pbegin, Tr)
58 {
59   GSList* 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_slist_prepend(head, const_cast<void*>(item));
66     ++pbegin;
67   }
68 
69   return g_slist_reverse(head);
70 }
71 
72 /* Convert from any container that supports bidirectional iterators.
73  */
74 template <class Tr, class Cont>
75 struct SListSourceTraits
76 {
get_dataSListSourceTraits77   static GSList* get_data(const Cont& cont)
78   {
79     return Glib::Container_Helpers::create_slist(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 SListSourceTraits<Tr, Cont*>
90 {
91   static GSList* get_data(const Cont* array)
92   {
93     return (array) ? Glib::Container_Helpers::create_slist(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 SListSourceTraits<Tr, const Cont*> : SListSourceTraits<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 SListSourceTraits<Tr, Cont[N]>
110 {
111   static GSList* get_data(const Cont* array)
112   {
113     return Glib::Container_Helpers::create_slist(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 SListSourceTraits<Tr, const Cont[N]> : SListSourceTraits<Tr, Cont[N]>
121 {
122 };
123 
124 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
125 
126 /**
127  * @ingroup ContHelpers
128  */
129 template <class Tr>
130 class SListHandleIterator
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 SListHandleIterator(const GSList* node);
143 
144   inline value_type operator*() const;
145   inline SListHandleIterator<Tr>& operator++();
146   inline const SListHandleIterator<Tr> operator++(int);
147 
148   inline bool operator==(const SListHandleIterator<Tr>& rhs) const;
149   inline bool operator!=(const SListHandleIterator<Tr>& rhs) const;
150 
151 private:
152   const GSList* 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  * @ingroup ContHandles
165  */
166 template <class T, class Tr = Glib::Container_Helpers::TypeTraits<T>>
167 class SListHandle
168 {
169 public:
170   using CppType = typename Tr::CppType;
171   using CType = typename Tr::CType;
172 
173   using value_type = CppType;
174   using size_type = std::size_t;
175   using difference_type = std::ptrdiff_t;
176 
177   using const_iterator = Glib::Container_Helpers::SListHandleIterator<Tr>;
178   using iterator = Glib::Container_Helpers::SListHandleIterator<Tr>;
179 
180   template <class Cont>
181   inline SListHandle(const Cont& container);
182 
183   // Take over ownership of a GSList created by GTK+ functions.
184   inline SListHandle(GSList* glist, Glib::OwnershipType ownership);
185 
186   // Copying clears the ownership flag of the source handle.
187   inline SListHandle(const SListHandle<T, Tr>& other);
188 
189   ~SListHandle() noexcept;
190 
191   inline const_iterator begin() const;
192   inline const_iterator end() const;
193 
194   template <class U>
195   inline operator std::vector<U>() const;
196   template <class U>
197   inline operator std::deque<U>() const;
198   template <class U>
199   inline operator std::list<U>() const;
200 
201   template <class Cont>
202   inline void assign_to(Cont& container) const;
203   template <class Out>
204   inline void copy(Out pdest) const;
205 
206   inline GSList* data() const;
207   inline std::size_t size() const;
208   inline bool empty() const;
209 
210 private:
211   GSList* pslist_;
212   mutable Glib::OwnershipType ownership_;
213 
214   // No copy assignment.
215   SListHandle<T, Tr>& operator=(const SListHandle<T, Tr>&);
216 };
217 
218 /***************************************************************************/
219 /*  Inline implementation                                                  */
220 /***************************************************************************/
221 
222 #ifndef DOXYGEN_SHOULD_SKIP_THIS
223 
224 namespace Container_Helpers
225 {
226 
227 /**** Glib::Container_Helpers::SListHandleIterator<> ***********************/
228 
229 template <class Tr>
230 inline SListHandleIterator<Tr>::SListHandleIterator(const GSList* node) : node_(node)
231 {
232 }
233 
234 template <class Tr>
235 inline typename SListHandleIterator<Tr>::value_type SListHandleIterator<Tr>::operator*() const
236 {
237   return Tr::to_cpp_type(static_cast<typename Tr::CTypeNonConst>(node_->data));
238 }
239 
240 template <class Tr>
241 inline SListHandleIterator<Tr>& SListHandleIterator<Tr>::operator++()
242 {
243   node_ = node_->next;
244   return *this;
245 }
246 
247 template <class Tr>
248 inline const SListHandleIterator<Tr> SListHandleIterator<Tr>::operator++(int)
249 {
250   const SListHandleIterator<Tr> tmp(*this);
251   node_ = node_->next;
252   return tmp;
253 }
254 
255 template <class Tr>
256 inline bool
257 SListHandleIterator<Tr>::operator==(const SListHandleIterator<Tr>& rhs) const
258 {
259   return (node_ == rhs.node_);
260 }
261 
262 template <class Tr>
263 inline bool
264 SListHandleIterator<Tr>::operator!=(const SListHandleIterator<Tr>& rhs) const
265 {
266   return (node_ != rhs.node_);
267 }
268 
269 } // namespace Container_Helpers
270 
271 /**** Glib::SListHandle<> **************************************************/
272 
273 template <class T, class Tr>
274 template <class Cont>
275 inline SListHandle<T, Tr>::SListHandle(const Cont& container)
276 : pslist_(Glib::Container_Helpers::SListSourceTraits<Tr, Cont>::get_data(container)),
277   ownership_(Glib::Container_Helpers::SListSourceTraits<Tr, Cont>::initial_ownership)
278 {
279 }
280 
281 template <class T, class Tr>
282 inline SListHandle<T, Tr>::SListHandle(GSList* gslist, Glib::OwnershipType ownership)
283 : pslist_(gslist), ownership_(ownership)
284 {
285 }
286 
287 template <class T, class Tr>
288 inline SListHandle<T, Tr>::SListHandle(const SListHandle<T, Tr>& other)
289 : pslist_(other.pslist_), ownership_(other.ownership_)
290 {
291   other.ownership_ = Glib::OWNERSHIP_NONE;
292 }
293 
294 template <class T, class Tr>
295 SListHandle<T, Tr>::~SListHandle() noexcept
296 {
297   if (ownership_ != Glib::OWNERSHIP_NONE)
298   {
299     if (ownership_ != Glib::OWNERSHIP_SHALLOW)
300     {
301       // Deep ownership: release each container element.
302       for (GSList* node = pslist_; node != nullptr; node = node->next)
303         Tr::release_c_type(static_cast<typename Tr::CTypeNonConst>(node->data));
304     }
305     g_slist_free(pslist_);
306   }
307 }
308 
309 template <class T, class Tr>
310 inline typename SListHandle<T, Tr>::const_iterator
311 SListHandle<T, Tr>::begin() const
312 {
313   return Glib::Container_Helpers::SListHandleIterator<Tr>(pslist_);
314 }
315 
316 template <class T, class Tr>
317 inline typename SListHandle<T, Tr>::const_iterator
318 SListHandle<T, Tr>::end() const
319 {
320   return Glib::Container_Helpers::SListHandleIterator<Tr>(nullptr);
321 }
322 
323 template <class T, class Tr>
324 template <class U>
325 inline SListHandle<T, Tr>::operator std::vector<U>() const
326 {
327 #ifdef GLIBMM_HAVE_TEMPLATE_SEQUENCE_CTORS
328   return std::vector<U>(this->begin(), this->end());
329 #else
330   std::vector<U> temp;
331   temp.reserve(this->size());
332   Glib::Container_Helpers::fill_container(temp, this->begin(), this->end());
333   return temp;
334 #endif
335 }
336 
337 template <class T, class Tr>
338 template <class U>
339 inline SListHandle<T, Tr>::operator std::deque<U>() const
340 {
341 #ifdef GLIBMM_HAVE_TEMPLATE_SEQUENCE_CTORS
342   return std::deque<U>(this->begin(), this->end());
343 #else
344   std::deque<U> temp;
345   Glib::Container_Helpers::fill_container(temp, this->begin(), this->end());
346   return temp;
347 #endif
348 }
349 
350 template <class T, class Tr>
351 template <class U>
352 inline SListHandle<T, Tr>::operator std::list<U>() const
353 {
354 #ifdef GLIBMM_HAVE_TEMPLATE_SEQUENCE_CTORS
355   return std::list<U>(this->begin(), this->end());
356 #else
357   std::list<U> temp;
358   Glib::Container_Helpers::fill_container(temp, this->begin(), this->end());
359   return temp;
360 #endif
361 }
362 
363 template <class T, class Tr>
364 template <class Cont>
365 inline void
366 SListHandle<T, Tr>::assign_to(Cont& container) const
367 {
368 #ifdef GLIBMM_HAVE_TEMPLATE_SEQUENCE_CTORS
369   container.assign(this->begin(), this->end());
370 #else
371   Cont temp;
372   Glib::Container_Helpers::fill_container(temp, this->begin(), this->end());
373   container.swap(temp);
374 #endif
375 }
376 
377 template <class T, class Tr>
378 template <class Out>
379 inline void
380 SListHandle<T, Tr>::copy(Out pdest) const
381 {
382   std::copy(this->begin(), this->end(), pdest);
383 }
384 
385 template <class T, class Tr>
386 inline GSList*
387 SListHandle<T, Tr>::data() const
388 {
389   return pslist_;
390 }
391 
392 template <class T, class Tr>
393 inline std::size_t
394 SListHandle<T, Tr>::size() const
395 {
396   return g_slist_length(pslist_);
397 }
398 
399 template <class T, class Tr>
400 inline bool
401 SListHandle<T, Tr>::empty() const
402 {
403   return (pslist_ == nullptr);
404 }
405 
406 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
407 
408 } // namespace Glib
409 
410 #endif /* _GLIBMM_SLISTHANDLE_H */
411