| // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIBCHROMEOS_CHROMEOS_GLIB_OBJECT_H_ |
| #define LIBCHROMEOS_CHROMEOS_GLIB_OBJECT_H_ |
| |
| #include <glib-object.h> |
| #include <stdint.h> |
| |
| #include <base/logging.h> |
| #include <base/macros.h> |
| #include <base/memory/scoped_ptr.h> |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <string> |
| |
| namespace chromeos { |
| |
| namespace details { // NOLINT |
| |
| // \brief ResetHelper is a private class for use with Resetter(). |
| // |
| // ResetHelper passes ownership of a pointer to a scoped pointer type with reset |
| // on destruction. |
| |
| template <typename T> // T models ScopedPtr |
| class ResetHelper { |
| public: |
| typedef typename T::element_type element_type; |
| |
| explicit ResetHelper(T* x) |
| : ptr_(nullptr), |
| scoped_(x) { |
| } |
| ~ResetHelper() { |
| scoped_->reset(ptr_); |
| } |
| element_type*& lvalue() { |
| return ptr_; |
| } |
| |
| private: |
| element_type* ptr_; |
| T* scoped_; |
| }; |
| |
| } // namespace details |
| |
| // \brief Resetter() is a utility function for passing pointers to |
| // scoped pointers. |
| // |
| // The Resetter() function return a temporary object containing an lvalue of |
| // \code T::element_type which can be assigned to. When the temporary object |
| // destructs, the associated scoped pointer is reset with the lvalue. It is of |
| // general use when a pointer is returned as an out-argument. |
| // |
| // \example |
| // void function(int** x) { |
| // *x = new int(10); |
| // } |
| // ... |
| // scoped_ptr<int> x; |
| // function(Resetter(x).lvalue()); |
| // |
| // \end_example |
| |
| template <typename T> // T models ScopedPtr |
| details::ResetHelper<T> Resetter(T* x) { |
| return details::ResetHelper<T>(x); |
| } |
| |
| // \precondition No functions in the glib namespace can be called before |
| // ::g_type_init(); |
| |
| namespace glib { |
| |
| // \brief type_to_gtypeid is a type function mapping from a canonical type to |
| // the GType typeid for the associated GType (see type_to_gtype). |
| |
| template <typename T> ::GType type_to_gtypeid(); |
| |
| template < > |
| inline ::GType type_to_gtypeid<const char*>() { |
| return G_TYPE_STRING; |
| } |
| template < > |
| inline ::GType type_to_gtypeid<char*>() { |
| return G_TYPE_STRING; |
| } |
| template < > |
| inline ::GType type_to_gtypeid< ::uint8_t>() { |
| return G_TYPE_UCHAR; |
| } |
| template < > |
| inline ::GType type_to_gtypeid<double>() { |
| return G_TYPE_DOUBLE; |
| } |
| template < > |
| inline ::GType type_to_gtypeid<bool>() { |
| return G_TYPE_BOOLEAN; |
| } |
| class Value; |
| template < > |
| inline ::GType type_to_gtypeid<const Value*>() { |
| return G_TYPE_VALUE; |
| } |
| |
| template < > |
| inline ::GType type_to_gtypeid< ::uint32_t>() { |
| // REVISIT (seanparent) : There currently isn't any G_TYPE_UINT32, this code |
| // assumes sizeof(guint) == sizeof(guint32). Need a static_assert to assert |
| // that. |
| return G_TYPE_UINT; |
| } |
| |
| template < > |
| inline ::GType type_to_gtypeid< ::int64_t>() { |
| return G_TYPE_INT64; |
| } |
| |
| template < > |
| inline ::GType type_to_gtypeid< ::int32_t>() { |
| return G_TYPE_INT; |
| } |
| |
| // \brief Value (and Retrieve) support using std::string as well as const char* |
| // by promoting from const char* to the string. promote_from provides a mapping |
| // for this promotion (and possibly others in the future). |
| |
| template <typename T> struct promotes_from { |
| typedef T type; |
| }; |
| template < > struct promotes_from<std::string> { |
| typedef const char* type; |
| }; |
| |
| // \brief RawCast converts from a GValue to a value of a canonical type. |
| // |
| // RawCast is a low level function. Generally, use Cast() instead. |
| // |
| // \precondition \param x contains a value of type \param T. |
| |
| template <typename T> |
| inline T RawCast(const ::GValue& x) { |
| // Use static_assert() to issue a meaningful compile-time error. |
| // To prevent this from happening for all references to RawCast, use sizeof(T) |
| // to make static_assert depend on type T and therefore prevent binding it |
| // unconditionally until the actual RawCast<T> instantiation happens. |
| static_assert(sizeof(T) == 0, "Using RawCast on unsupported type"); |
| return T(); |
| } |
| |
| template < > |
| inline const char* RawCast<const char*>(const ::GValue& x) { |
| return static_cast<const char*>(::g_value_get_string(&x)); |
| } |
| template < > |
| inline double RawCast<double>(const ::GValue& x) { |
| return static_cast<double>(::g_value_get_double(&x)); |
| } |
| template < > |
| inline bool RawCast<bool>(const ::GValue& x) { |
| return static_cast<bool>(::g_value_get_boolean(&x)); |
| } |
| template < > |
| inline ::uint32_t RawCast< ::uint32_t>(const ::GValue& x) { |
| return static_cast< ::uint32_t>(::g_value_get_uint(&x)); |
| } |
| template < > |
| inline ::uint8_t RawCast< ::uint8_t>(const ::GValue& x) { |
| return static_cast< ::uint8_t>(::g_value_get_uchar(&x)); |
| } |
| template < > |
| inline ::int64_t RawCast< ::int64_t>(const ::GValue& x) { |
| return static_cast< ::int64_t>(::g_value_get_int64(&x)); |
| } |
| template < > |
| inline ::int32_t RawCast< ::int32_t>(const ::GValue& x) { |
| return static_cast< ::int32_t>(::g_value_get_int(&x)); |
| } |
| |
| inline void RawSet(GValue* x, const std::string& v) { |
| ::g_value_set_string(x, v.c_str()); |
| } |
| inline void RawSet(GValue* x, const char* v) { |
| ::g_value_set_string(x, v); |
| } |
| inline void RawSet(GValue* x, double v) { |
| ::g_value_set_double(x, v); |
| } |
| inline void RawSet(GValue* x, bool v) { |
| ::g_value_set_boolean(x, v); |
| } |
| inline void RawSet(GValue* x, ::uint32_t v) { |
| ::g_value_set_uint(x, v); |
| } |
| inline void RawSet(GValue* x, ::uint8_t v) { |
| ::g_value_set_uchar(x, v); |
| } |
| inline void RawSet(GValue* x, ::int64_t v) { |
| ::g_value_set_int64(x, v); |
| } |
| inline void RawSet(GValue* x, ::int32_t v) { |
| ::g_value_set_int(x, v); |
| } |
| |
| // \brief Value is a data type for managing GValues. |
| // |
| // A Value is a polymorphic container holding at most a single value. |
| // |
| // The Value wrapper ensures proper initialization, copies, and assignment of |
| // GValues. |
| // |
| // \note GValues are equationally incomplete and so can't support proper |
| // equality. The semantics of copy are verified with equality of retrieved |
| // values. |
| |
| class Value : public ::GValue { |
| public: |
| Value() |
| : GValue() { |
| } |
| explicit Value(const ::GValue& x) |
| : GValue() { |
| *this = *static_cast<const Value*>(&x); |
| } |
| template <typename T> |
| explicit Value(T x) |
| : GValue() { |
| ::g_value_init(this, |
| type_to_gtypeid<typename promotes_from<T>::type>()); |
| RawSet(this, x); |
| } |
| Value(const Value& x) |
| : GValue() { |
| if (x.empty()) |
| return; |
| ::g_value_init(this, G_VALUE_TYPE(&x)); |
| ::g_value_copy(&x, this); |
| } |
| ~Value() { |
| clear(); |
| } |
| Value& operator=(const Value& x) { |
| if (this == &x) |
| return *this; |
| clear(); |
| if (x.empty()) |
| return *this; |
| ::g_value_init(this, G_VALUE_TYPE(&x)); |
| ::g_value_copy(&x, this); |
| return *this; |
| } |
| template <typename T> |
| Value& operator=(const T& x) { |
| clear(); |
| ::g_value_init(this, |
| type_to_gtypeid<typename promotes_from<T>::type>()); |
| RawSet(this, x); |
| return *this; |
| } |
| |
| // Lower-case names to follow STL container conventions. |
| |
| void clear() { |
| if (!empty()) |
| ::g_value_unset(this); |
| } |
| |
| bool empty() const { |
| return G_VALUE_TYPE(this) == G_TYPE_INVALID; |
| } |
| }; |
| |
| template < > |
| inline const Value* RawCast<const Value*>(const ::GValue& x) { |
| return static_cast<const Value*>(&x); |
| } |
| |
| // \brief Retrieve gets a value from a GValue. |
| // |
| // \postcondition If \param x contains a value of type \param T, then the |
| // value is copied to \param result and \true is returned. Otherwise, \param |
| // result is unchanged and \false is returned. |
| // |
| // \precondition \param result is not \nullptr. |
| |
| template <typename T> |
| bool Retrieve(const ::GValue& x, T* result) { |
| if (!G_VALUE_HOLDS(&x, type_to_gtypeid<typename promotes_from<T>::type>())) { |
| LOG(WARNING) << "GValue retrieve failed. Expected: " |
| << g_type_name(type_to_gtypeid<typename promotes_from<T>::type>()) |
| << ", Found: " << g_type_name(G_VALUE_TYPE(&x)); |
| return false; |
| } |
| |
| *result = RawCast<typename promotes_from<T>::type>(x); |
| return true; |
| } |
| |
| inline bool Retrieve(const ::GValue& x, Value* result) { |
| *result = Value(x); |
| return true; |
| } |
| |
| // \brief ScopedError holds a ::GError* and deletes it on destruction. |
| |
| struct FreeError { |
| void operator()(::GError* x) const { |
| if (x) |
| ::g_error_free(x); |
| } |
| }; |
| |
| typedef ::scoped_ptr< ::GError, FreeError> ScopedError; |
| |
| // \brief ScopedArray holds a ::GArray* and deletes both the container and the |
| // segment containing the elements on destruction. |
| |
| struct FreeArray { |
| void operator()(::GArray* x) const { |
| if (x) |
| ::g_array_free(x, TRUE); |
| } |
| }; |
| |
| typedef ::scoped_ptr< ::GArray, FreeArray> ScopedArray; |
| |
| // \brief ScopedPtrArray adapts ::GPtrArray* to conform to the standard |
| // container requirements. |
| // |
| // \note ScopedPtrArray is only partially implemented and is being fleshed out |
| // as needed. |
| // |
| // \models Random Access Container, Back Insertion Sequence, ScopedPtrArray is |
| // not copyable and equationally incomplete. |
| |
| template <typename T> // T models pointer |
| class ScopedPtrArray { |
| public: |
| typedef ::GPtrArray element_type; |
| |
| typedef T value_type; |
| typedef const value_type& const_reference; |
| typedef value_type* iterator; |
| typedef const value_type* const_iterator; |
| |
| ScopedPtrArray() |
| : object_(0) { |
| } |
| |
| explicit ScopedPtrArray(::GPtrArray* x) |
| : object_(x) { |
| } |
| |
| ~ScopedPtrArray() { |
| clear(); |
| } |
| |
| iterator begin() { |
| return iterator(object_ ? object_->pdata : nullptr); |
| } |
| iterator end() { |
| return begin() + size(); |
| } |
| const_iterator begin() const { |
| return const_iterator(object_ ? object_->pdata : nullptr); |
| } |
| const_iterator end() const { |
| return begin() + size(); |
| } |
| |
| // \precondition x is a pointer to an object allocated with g_new(). |
| |
| void push_back(T x) { |
| if (!object_) |
| object_ = ::g_ptr_array_sized_new(1); |
| ::g_ptr_array_add(object_, ::gpointer(x)); |
| } |
| |
| T& operator[](std::size_t n) { |
| DCHECK(!(size() < n)) << "ScopedPtrArray index out-of-bound."; |
| return *(begin() + n); |
| } |
| |
| std::size_t size() const { |
| return object_ ? object_->len : 0; |
| } |
| |
| void clear() { |
| if (object_) { |
| std::for_each(begin(), end(), FreeHelper()); |
| ::g_ptr_array_free(object_, true); |
| object_ = nullptr; |
| } |
| } |
| |
| void reset(::GPtrArray* p = nullptr) { |
| if (p != object_) { |
| clear(); |
| object_ = p; |
| } |
| } |
| |
| private: |
| struct FreeHelper { |
| void operator()(T x) const { |
| ::g_free(::gpointer(x)); |
| } |
| }; |
| |
| template <typename U> |
| friend void swap(ScopedPtrArray<U>& x, ScopedPtrArray<U>& y); |
| |
| ::GPtrArray* object_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedPtrArray); |
| }; |
| |
| template <typename U> |
| inline void swap(ScopedPtrArray<U>& x, ScopedPtrArray<U>& y) { |
| std::swap(x.object_, y.object_); |
| } |
| |
| // \brief ScopedHashTable manages the lifetime of a ::GHashTable* with an |
| // interface compatibitle with a scoped ptr. |
| // |
| // The ScopedHashTable is also the start of an adaptor to model a standard |
| // Container. The standard for an associative container would have an iterator |
| // returning a key value pair. However, that isn't possible with |
| // ::GHashTable because there is no interface returning a reference to the |
| // key value pair, only to retrieve the keys and values and individual elements. |
| // |
| // So the standard interface of find() wouldn't work. I considered implementing |
| // operator[] and count() - operator []. So retrieving a value would look like: |
| // |
| // if (table.count(key)) |
| // success = Retrieve(table[key], &value); |
| // |
| // But that requires hashing the key twice. |
| // For now I implemented a Retrieve member function to follow the pattern |
| // developed elsewhere in the code. |
| // |
| // bool success = Retrieve(key, &x); |
| // |
| // This is also a template to retrieve the corect type from the stored GValue |
| // type. |
| // |
| // I may revisit this and use scoped_ptr_malloc and a non-member function |
| // Retrieve() in the future. The Retrieve pattern is becoming common enough |
| // that I want to give some thought as to how to generalize it further. |
| |
| class ScopedHashTable { |
| public: |
| typedef ::GHashTable element_type; |
| |
| ScopedHashTable() |
| : object_(nullptr) { |
| } |
| |
| explicit ScopedHashTable(::GHashTable* p) |
| : object_(p) { |
| } |
| |
| ~ScopedHashTable() { |
| clear(); |
| } |
| |
| template <typename T> |
| bool Retrieve(const char* key, T* result) const { |
| DCHECK(object_) << "Retrieve on empty ScopedHashTable."; |
| if (!object_) |
| return false; |
| |
| ::gpointer ptr = ::g_hash_table_lookup(object_, key); |
| if (!ptr) |
| return false; |
| return glib::Retrieve(*static_cast< ::GValue*>(ptr), result); |
| } |
| |
| void clear() { |
| if (object_) { |
| ::g_hash_table_unref(object_); |
| object_ = nullptr; |
| } |
| } |
| |
| GHashTable* get() { |
| return object_; |
| } |
| |
| void reset(::GHashTable* p = nullptr) { |
| if (p != object_) { |
| clear(); |
| object_ = p; |
| } |
| } |
| |
| private: |
| ::GHashTable* object_; |
| }; |
| |
| } // namespace glib |
| } // namespace chromeos |
| |
| #endif // LIBCHROMEOS_CHROMEOS_GLIB_OBJECT_H_ |