// 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 LIBBRILLO_BRILLO_GLIB_OBJECT_H_ #define LIBBRILLO_BRILLO_GLIB_OBJECT_H_ #include #include #include #include #include #include #include #include namespace brillo { 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 // 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); // } // ... // std::unique_ptr x; // function(Resetter(x).lvalue()); // // \end_example template // T models ScopedPtr details::ResetHelper Resetter(T* x) { return details::ResetHelper(x); } 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 ::GType type_to_gtypeid(); template < > inline ::GType type_to_gtypeid() { return G_TYPE_STRING; } template < > inline ::GType type_to_gtypeid() { return G_TYPE_STRING; } template < > inline ::GType type_to_gtypeid< ::uint8_t>() { return G_TYPE_UCHAR; } template < > inline ::GType type_to_gtypeid() { return G_TYPE_DOUBLE; } template < > inline ::GType type_to_gtypeid() { return G_TYPE_BOOLEAN; } class Value; template < > inline ::GType type_to_gtypeid() { 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 struct promotes_from { typedef T type; }; template < > struct promotes_from { 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 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 instantiation happens. static_assert(sizeof(T) == 0, "Using RawCast on unsupported type"); return T(); } template < > inline const char* RawCast(const ::GValue& x) { return static_cast(::g_value_get_string(&x)); } template < > inline double RawCast(const ::GValue& x) { return static_cast(::g_value_get_double(&x)); } template < > inline bool RawCast(const ::GValue& x) { return static_cast(::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(&x); } template explicit Value(T x) : GValue() { ::g_value_init(this, type_to_gtypeid::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 Value& operator=(const T& x) { clear(); ::g_value_init(this, type_to_gtypeid::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 ::GValue& x) { return static_cast(&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 bool Retrieve(const ::GValue& x, T* result) { if (!G_VALUE_HOLDS(&x, type_to_gtypeid::type>())) { LOG(WARNING) << "GValue retrieve failed. Expected: " << g_type_name(type_to_gtypeid::type>()) << ", Found: " << g_type_name(G_VALUE_TYPE(&x)); return false; } *result = RawCast::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 std::unique_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 std::unique_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 // 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 friend void swap(ScopedPtrArray& x, ScopedPtrArray& y); ::GPtrArray* object_; DISALLOW_COPY_AND_ASSIGN(ScopedPtrArray); }; template inline void swap(ScopedPtrArray& x, ScopedPtrArray& 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 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 brillo #endif // LIBBRILLO_BRILLO_GLIB_OBJECT_H_