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