xref: /aosp_15_r20/external/libbrillo/brillo/glib/object.h (revision 1a96fba65179ea7d3f56207137718607415c5953)
1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef LIBBRILLO_BRILLO_GLIB_OBJECT_H_
6 #define LIBBRILLO_BRILLO_GLIB_OBJECT_H_
7 
8 #include <glib-object.h>
9 #include <stdint.h>
10 
11 #include <algorithm>
12 #include <cstddef>
13 #include <memory>
14 #include <string>
15 #include <utility>
16 
17 #include <base/logging.h>
18 #include <base/macros.h>
19 
20 namespace brillo {
21 
22 namespace details {  // NOLINT
23 
24 // \brief ResetHelper is a private class for use with Resetter().
25 //
26 // ResetHelper passes ownership of a pointer to a scoped pointer type with reset
27 // on destruction.
28 
29 template <typename T>  // T models ScopedPtr
30 class ResetHelper {
31  public:
32   typedef typename T::element_type element_type;
33 
ResetHelper(T * x)34   explicit ResetHelper(T* x)
35       : ptr_(nullptr),
36         scoped_(x) {
37   }
~ResetHelper()38   ~ResetHelper() {
39     scoped_->reset(ptr_);
40   }
lvalue()41   element_type*& lvalue() {
42     return ptr_;
43   }
44 
45  private:
46   element_type* ptr_;
47   T* scoped_;
48 };
49 
50 }  // namespace details
51 
52 // \brief Resetter() is a utility function for passing pointers to
53 //  scoped pointers.
54 //
55 // The Resetter() function return a temporary object containing an lvalue of
56 // \code T::element_type which can be assigned to. When the temporary object
57 // destructs, the associated scoped pointer is reset with the lvalue. It is of
58 // general use when a pointer is returned as an out-argument.
59 //
60 // \example
61 // void function(int** x) {
62 //   *x = new int(10);
63 // }
64 // ...
65 // std::unique_ptr<int> x;
66 // function(Resetter(x).lvalue());
67 //
68 // \end_example
69 
70 template <typename T>  // T models ScopedPtr
Resetter(T * x)71 details::ResetHelper<T> Resetter(T* x) {
72   return details::ResetHelper<T>(x);
73 }
74 
75 namespace glib {
76 
77 // \brief type_to_gtypeid is a type function mapping from a canonical type to
78 // the GType typeid for the associated GType (see type_to_gtype).
79 
80 template <typename T> ::GType type_to_gtypeid();
81 
82 template < >
83 inline ::GType type_to_gtypeid<const char*>() {
84   return G_TYPE_STRING;
85 }
86 template < >
87 inline ::GType type_to_gtypeid<char*>() {
88   return G_TYPE_STRING;
89 }
90 template < >
91 inline ::GType type_to_gtypeid< ::uint8_t>() {
92   return G_TYPE_UCHAR;
93 }
94 template < >
95 inline ::GType type_to_gtypeid<double>() {
96   return G_TYPE_DOUBLE;
97 }
98 template < >
99 inline ::GType type_to_gtypeid<bool>() {
100   return G_TYPE_BOOLEAN;
101 }
102 class Value;
103 template < >
104 inline ::GType type_to_gtypeid<const Value*>() {
105   return G_TYPE_VALUE;
106 }
107 
108 template < >
109 inline ::GType type_to_gtypeid< ::uint32_t>() {
110   // REVISIT (seanparent) : There currently isn't any G_TYPE_UINT32, this code
111   // assumes sizeof(guint) == sizeof(guint32). Need a static_assert to assert
112   // that.
113   return G_TYPE_UINT;
114 }
115 
116 template < >
117 inline ::GType type_to_gtypeid< ::int64_t>() {
118   return G_TYPE_INT64;
119 }
120 
121 template < >
122 inline ::GType type_to_gtypeid< ::int32_t>() {
123   return G_TYPE_INT;
124 }
125 
126 // \brief Value (and Retrieve) support using std::string as well as const char*
127 // by promoting from const char* to the string. promote_from provides a mapping
128 // for this promotion (and possibly others in the future).
129 
130 template <typename T> struct promotes_from {
131   typedef T type;
132 };
133 template < > struct promotes_from<std::string> {
134   typedef const char* type;
135 };
136 
137 // \brief RawCast converts from a GValue to a value of a canonical type.
138 //
139 // RawCast is a low level function. Generally, use Cast() instead.
140 //
141 // \precondition \param x contains a value of type \param T.
142 
143 template <typename T>
144 inline T RawCast(const ::GValue& x) {
145   // Use static_assert() to issue a meaningful compile-time error.
146   // To prevent this from happening for all references to RawCast, use sizeof(T)
147   // to make static_assert depend on type T and therefore prevent binding it
148   // unconditionally until the actual RawCast<T> instantiation happens.
149   static_assert(sizeof(T) == 0, "Using RawCast on unsupported type");
150   return T();
151 }
152 
153 template < >
154 inline const char* RawCast<const char*>(const ::GValue& x) {
155   return static_cast<const char*>(::g_value_get_string(&x));
156 }
157 template < >
158 inline double RawCast<double>(const ::GValue& x) {
159   return static_cast<double>(::g_value_get_double(&x));
160 }
161 template < >
162 inline bool RawCast<bool>(const ::GValue& x) {
163   return static_cast<bool>(::g_value_get_boolean(&x));
164 }
165 template < >
166 inline ::uint32_t RawCast< ::uint32_t>(const ::GValue& x) {
167   return static_cast< ::uint32_t>(::g_value_get_uint(&x));
168 }
169 template < >
170 inline ::uint8_t RawCast< ::uint8_t>(const ::GValue& x) {
171   return static_cast< ::uint8_t>(::g_value_get_uchar(&x));
172 }
173 template < >
174 inline ::int64_t RawCast< ::int64_t>(const ::GValue& x) {
175   return static_cast< ::int64_t>(::g_value_get_int64(&x));
176 }
177 template < >
178 inline ::int32_t RawCast< ::int32_t>(const ::GValue& x) {
179   return static_cast< ::int32_t>(::g_value_get_int(&x));
180 }
181 
182 inline void RawSet(GValue* x, const std::string& v) {
183   ::g_value_set_string(x, v.c_str());
184 }
185 inline void RawSet(GValue* x, const char* v) {
186   ::g_value_set_string(x, v);
187 }
188 inline void RawSet(GValue* x, double v) {
189   ::g_value_set_double(x, v);
190 }
191 inline void RawSet(GValue* x, bool v) {
192   ::g_value_set_boolean(x, v);
193 }
194 inline void RawSet(GValue* x, ::uint32_t v) {
195   ::g_value_set_uint(x, v);
196 }
197 inline void RawSet(GValue* x, ::uint8_t v) {
198   ::g_value_set_uchar(x, v);
199 }
200 inline void RawSet(GValue* x, ::int64_t v) {
201   ::g_value_set_int64(x, v);
202 }
203 inline void RawSet(GValue* x, ::int32_t v) {
204   ::g_value_set_int(x, v);
205 }
206 
207 // \brief Value is a data type for managing GValues.
208 //
209 // A Value is a polymorphic container holding at most a single value.
210 //
211 // The Value wrapper ensures proper initialization, copies, and assignment of
212 // GValues.
213 //
214 // \note GValues are equationally incomplete and so can't support proper
215 // equality. The semantics of copy are verified with equality of retrieved
216 // values.
217 
218 class Value : public ::GValue {
219  public:
220   Value()
221       : GValue() {
222   }
223   explicit Value(const ::GValue& x)
224       : GValue() {
225     *this = *static_cast<const Value*>(&x);
226   }
227   template <typename T>
228   explicit Value(T x)
229       : GValue() {
230     ::g_value_init(this,
231         type_to_gtypeid<typename promotes_from<T>::type>());
232     RawSet(this, x);
233   }
234   Value(const Value& x)
235       : GValue() {
236     if (x.empty())
237       return;
238     ::g_value_init(this, G_VALUE_TYPE(&x));
239     ::g_value_copy(&x, this);
240   }
241   ~Value() {
242     clear();
243   }
244   Value& operator=(const Value& x) {
245     if (this == &x)
246       return *this;
247     clear();
248     if (x.empty())
249       return *this;
250     ::g_value_init(this, G_VALUE_TYPE(&x));
251     ::g_value_copy(&x, this);
252     return *this;
253   }
254   template <typename T>
255   Value& operator=(const T& x) {
256     clear();
257     ::g_value_init(this,
258                    type_to_gtypeid<typename promotes_from<T>::type>());
259     RawSet(this, x);
260     return *this;
261   }
262 
263   // Lower-case names to follow STL container conventions.
264 
265   void clear() {
266     if (!empty())
267       ::g_value_unset(this);
268   }
269 
270   bool empty() const {
271     return G_VALUE_TYPE(this) == G_TYPE_INVALID;
272   }
273 };
274 
275 template < >
276 inline const Value* RawCast<const Value*>(const ::GValue& x) {
277   return static_cast<const Value*>(&x);
278 }
279 
280 // \brief Retrieve gets a value from a GValue.
281 //
282 // \postcondition If \param x contains a value of type \param T, then the
283 //  value is copied to \param result and \true is returned. Otherwise, \param
284 //  result is unchanged and \false is returned.
285 //
286 // \precondition \param result is not \nullptr.
287 
288 template <typename T>
289 bool Retrieve(const ::GValue& x, T* result) {
290   if (!G_VALUE_HOLDS(&x, type_to_gtypeid<typename promotes_from<T>::type>())) {
291     LOG(WARNING) << "GValue retrieve failed. Expected: "
292         << g_type_name(type_to_gtypeid<typename promotes_from<T>::type>())
293         << ", Found: " << g_type_name(G_VALUE_TYPE(&x));
294     return false;
295   }
296 
297   *result = RawCast<typename promotes_from<T>::type>(x);
298   return true;
299 }
300 
301 inline bool Retrieve(const ::GValue& x, Value* result) {
302   *result = Value(x);
303   return true;
304 }
305 
306 // \brief ScopedError holds a ::GError* and deletes it on destruction.
307 
308 struct FreeError {
309   void operator()(::GError* x) const {
310     if (x)
311       ::g_error_free(x);
312   }
313 };
314 
315 typedef std::unique_ptr< ::GError, FreeError> ScopedError;
316 
317 // \brief ScopedArray holds a ::GArray* and deletes both the container and the
318 // segment containing the elements on destruction.
319 
320 struct FreeArray {
321   void operator()(::GArray* x) const {
322     if (x)
323       ::g_array_free(x, TRUE);
324   }
325 };
326 
327 typedef std::unique_ptr< ::GArray, FreeArray> ScopedArray;
328 
329 // \brief ScopedPtrArray adapts ::GPtrArray* to conform to the standard
330 //  container requirements.
331 //
332 // \note ScopedPtrArray is only partially implemented and is being fleshed out
333 //  as needed.
334 //
335 // \models Random Access Container, Back Insertion Sequence, ScopedPtrArray is
336 //  not copyable and equationally incomplete.
337 
338 template <typename T>  // T models pointer
339 class ScopedPtrArray {
340  public:
341   typedef ::GPtrArray element_type;
342 
343   typedef T value_type;
344   typedef const value_type& const_reference;
345   typedef value_type* iterator;
346   typedef const value_type* const_iterator;
347 
348   ScopedPtrArray()
349       : object_(0) {
350   }
351 
352   explicit ScopedPtrArray(::GPtrArray* x)
353       : object_(x) {
354   }
355 
356   ~ScopedPtrArray() {
357     clear();
358   }
359 
360   iterator begin() {
361     return iterator(object_ ? object_->pdata : nullptr);
362   }
363   iterator end() {
364     return begin() + size();
365   }
366   const_iterator begin() const {
367     return const_iterator(object_ ? object_->pdata : nullptr);
368   }
369   const_iterator end() const {
370     return begin() + size();
371   }
372 
373   // \precondition x is a pointer to an object allocated with g_new().
374 
375   void push_back(T x) {
376     if (!object_)
377       object_ = ::g_ptr_array_sized_new(1);
378     ::g_ptr_array_add(object_, ::gpointer(x));
379   }
380 
381   T& operator[](std::size_t n) {
382     DCHECK(!(size() < n)) << "ScopedPtrArray index out-of-bound.";
383     return *(begin() + n);
384   }
385 
386   std::size_t size() const {
387     return object_ ? object_->len : 0;
388   }
389 
390   void clear() {
391     if (object_) {
392       std::for_each(begin(), end(), FreeHelper());
393       ::g_ptr_array_free(object_, true);
394       object_ = nullptr;
395     }
396   }
397 
398   void reset(::GPtrArray* p = nullptr) {
399     if (p != object_) {
400       clear();
401       object_ = p;
402     }
403   }
404 
405  private:
406   struct FreeHelper {
407     void operator()(T x) const {
408       ::g_free(::gpointer(x));
409     }
410   };
411 
412   template <typename U>
413   friend void swap(ScopedPtrArray<U>& x, ScopedPtrArray<U>& y);
414 
415   ::GPtrArray* object_;
416 
417   DISALLOW_COPY_AND_ASSIGN(ScopedPtrArray);
418 };
419 
420 template <typename U>
421 inline void swap(ScopedPtrArray<U>& x, ScopedPtrArray<U>& y) {
422   std::swap(x.object_, y.object_);
423 }
424 
425 // \brief ScopedHashTable manages the lifetime of a ::GHashTable* with an
426 // interface compatibitle with a scoped ptr.
427 //
428 // The ScopedHashTable is also the start of an adaptor to model a standard
429 // Container. The standard for an associative container would have an iterator
430 // returning a key value pair. However, that isn't possible with
431 // ::GHashTable because there is no interface returning a reference to the
432 // key value pair, only to retrieve the keys and values and individual elements.
433 //
434 // So the standard interface of find() wouldn't work. I considered implementing
435 // operator[] and count() - operator []. So retrieving a value would look like:
436 //
437 // if (table.count(key))
438 //   success = Retrieve(table[key], &value);
439 //
440 // But that requires hashing the key twice.
441 // For now I implemented a Retrieve member function to follow the pattern
442 // developed elsewhere in the code.
443 //
444 // bool success = Retrieve(key, &x);
445 //
446 // This is also a template to retrieve the corect type from the stored GValue
447 // type.
448 //
449 // I may revisit this and use scoped_ptr_malloc and a non-member function
450 // Retrieve() in the future. The Retrieve pattern is becoming common enough
451 // that I want to give some thought as to how to generalize it further.
452 
453 class ScopedHashTable {
454  public:
455   typedef ::GHashTable element_type;
456 
457   ScopedHashTable()
458       : object_(nullptr) {
459   }
460 
461   explicit ScopedHashTable(::GHashTable* p)
462       : object_(p) {
463   }
464 
465   ~ScopedHashTable() {
466     clear();
467   }
468 
469   template <typename T>
470   bool Retrieve(const char* key, T* result) const {
471     DCHECK(object_) << "Retrieve on empty ScopedHashTable.";
472     if (!object_)
473       return false;
474 
475     ::gpointer ptr = ::g_hash_table_lookup(object_, key);
476     if (!ptr)
477       return false;
478     return glib::Retrieve(*static_cast< ::GValue*>(ptr), result);
479   }
480 
481   void clear() {
482     if (object_) {
483       ::g_hash_table_unref(object_);
484       object_ = nullptr;
485     }
486   }
487 
488   GHashTable* get() {
489     return object_;
490   }
491 
492   void reset(::GHashTable* p = nullptr) {
493     if (p != object_) {
494       clear();
495       object_ = p;
496     }
497   }
498 
499  private:
500   ::GHashTable* object_;
501 };
502 
503 }  // namespace glib
504 }  // namespace brillo
505 
506 #endif  // LIBBRILLO_BRILLO_GLIB_OBJECT_H_
507