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