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