Home | History | Annotate | Download | only in brillo
      1 // Copyright 2014 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 // This is an implementation of a "true" variant class in C++.
      6 // The brillo::Any class can hold any C++ type, but both the setter and
      7 // getter sites need to know the actual type of data.
      8 // Note that C-style arrays when stored in Any are reduced to simple
      9 // data pointers. Any will not copy a contents of the array.
     10 //    const int data[] = [1,2,3];
     11 //    Any v(data);  // stores const int*, effectively "Any v(&data[0]);"
     12 
     13 // brillo::Any is a value type. Which means, the data is copied into it
     14 // and Any owns it. The owned object (stored by value) will be destroyed
     15 // when Any is cleared or reassigned. The contained value type must be
     16 // copy-constructible. You can also store pointers and references to objects.
     17 // Storing pointers is trivial. In order to store a reference, you can
     18 // use helper functions std::ref() and std::cref() to create non-const and
     19 // const references respectively. In such a case, the type of contained data
     20 // will be std::reference_wrapper<T>. See 'References' unit tests in
     21 // any_unittest.cc for examples.
     22 
     23 #ifndef LIBBRILLO_BRILLO_ANY_H_
     24 #define LIBBRILLO_BRILLO_ANY_H_
     25 
     26 #include <brillo/any_internal_impl.h>
     27 
     28 #include <algorithm>
     29 
     30 #include <brillo/brillo_export.h>
     31 #include <brillo/type_name_undecorate.h>
     32 
     33 namespace dbus {
     34 class MessageWriter;
     35 }  // namespace dbus
     36 
     37 namespace brillo {
     38 
     39 class BRILLO_EXPORT Any final {
     40  public:
     41   Any();  // Do not inline to hide internal_details::Buffer from export table.
     42   // Standard copy/move constructors. This is a value-class container
     43   // that must be copy-constructible and movable. The copy constructors
     44   // should not be marked as explicit.
     45   Any(const Any& rhs);
     46   Any(Any&& rhs);  // NOLINT(build/c++11)
     47   // Typed constructor that stores a value of type T in the Any.
     48   template<class T>
     49   inline Any(T value) {  // NOLINT(runtime/explicit)
     50     data_buffer_.Assign(std::move(value));
     51   }
     52 
     53   // Not declaring the destructor as virtual since this is a sealed class
     54   // and there is no need to introduce a virtual table to it.
     55   ~Any();
     56 
     57   // Assignment operators.
     58   Any& operator=(const Any& rhs);
     59   Any& operator=(Any&& rhs);  // NOLINT(build/c++11)
     60   template<class T>
     61   inline Any& operator=(T value) {
     62     data_buffer_.Assign(std::move(value));
     63     return *this;
     64   }
     65 
     66   // Compares the contents of two Any objects for equality. Note that the
     67   // contained type must be equality-comparable (must have operator== defined).
     68   // If operator==() is not available for contained type, comparison operation
     69   // always returns false (as if the data were different).
     70   bool operator==(const Any& rhs) const;
     71   inline bool operator!=(const Any& rhs) const { return !operator==(rhs); }
     72 
     73   // Checks if the given type DestType can be obtained from the Any.
     74   // For example, to check if Any has a 'double' value in it:
     75   //  any.IsTypeCompatible<double>()
     76   template<typename DestType>
     77   bool IsTypeCompatible() const {
     78     // Make sure the requested type DestType conforms to the storage
     79     // requirements of Any. We always store the data by value, which means we
     80     // strip away any references as well as cv-qualifiers. So, if the user
     81     // stores "const int&", we actually store just an "int".
     82     // When calling IsTypeCompatible, we need to do a similar "type cleansing"
     83     // to make sure the requested type matches the type of data actually stored,
     84     // so this "canonical" type is used for type checking below.
     85     using CanonicalDestType = typename std::decay<DestType>::type;
     86     const char* contained_type = GetTypeTagInternal();
     87     if (strcmp(GetTypeTag<CanonicalDestType>(), contained_type) == 0)
     88       return true;
     89 
     90     if (!std::is_pointer<CanonicalDestType>::value)
     91       return false;
     92 
     93     // If asking for a const pointer from a variant containing non-const
     94     // pointer, still satisfy the request. So, we need to remove the pointer
     95     // specification first, then strip the const/volatile qualifiers, then
     96     // re-add the pointer back, so "const int*" would become "int*".
     97     using NonPointer = typename std::remove_pointer<CanonicalDestType>::type;
     98     using CanonicalDestTypeNoConst = typename std::add_pointer<
     99         typename std::remove_const<NonPointer>::type>::type;
    100     if (strcmp(GetTypeTag<CanonicalDestTypeNoConst>(), contained_type) == 0)
    101       return true;
    102 
    103     using CanonicalDestTypeNoVolatile = typename std::add_pointer<
    104         typename std::remove_volatile<NonPointer>::type>::type;
    105     if (strcmp(GetTypeTag<CanonicalDestTypeNoVolatile>(), contained_type) == 0)
    106       return true;
    107 
    108     using CanonicalDestTypeNoConstOrVolatile = typename std::add_pointer<
    109         typename std::remove_cv<NonPointer>::type>::type;
    110     return strcmp(GetTypeTag<CanonicalDestTypeNoConstOrVolatile>(),
    111                   contained_type) == 0;
    112   }
    113 
    114   // Returns immutable data contained in Any.
    115   // Aborts if Any doesn't contain a value of type T, or trivially
    116   // convertible to/compatible with it.
    117   template<typename T>
    118   const T& Get() const {
    119     CHECK(IsTypeCompatible<T>())
    120         << "Requesting value of type '" << brillo::GetUndecoratedTypeName<T>()
    121         << "' from variant containing '" << GetUndecoratedTypeName()
    122         << "'";
    123     return data_buffer_.GetData<T>();
    124   }
    125 
    126   // Returns a copy of data in Any and returns true when that data is
    127   // compatible with T.  Returns false if contained data is incompatible.
    128   template<typename T>
    129   bool GetValue(T* value) const {
    130     if (!IsTypeCompatible<T>()) {
    131       return false;
    132     }
    133     *value = Get<T>();
    134     return true;
    135   }
    136 
    137   // Returns a pointer to mutable value of type T contained within Any.
    138   // No data copying is made, the data pointed to is still owned by Any.
    139   // If Any doesn't contain a value of type T, or trivially
    140   // convertible/compatible to/with it, then it returns nullptr.
    141   template<typename T>
    142   T* GetPtr() {
    143     if (!IsTypeCompatible<T>())
    144       return nullptr;
    145     return &(data_buffer_.GetData<T>());
    146   }
    147 
    148   // Returns a copy of the data contained in Any.
    149   // If the Any doesn't contain a compatible value, the provided default
    150   // |def_val| is returned instead.
    151   template<typename T>
    152   T TryGet(typename std::decay<T>::type const& def_val) const {
    153     if (!IsTypeCompatible<T>())
    154       return def_val;
    155     return data_buffer_.GetData<T>();
    156   }
    157 
    158   // A convenience specialization of the above function where the default
    159   // value of type T is returned in case the underlying Get() fails.
    160   template<typename T>
    161   T TryGet() const {
    162     return TryGet<T>(typename std::decay<T>::type());
    163   }
    164 
    165   // Returns the undecorated name of the type contained within Any.
    166   inline std::string GetUndecoratedTypeName() const {
    167     return GetUndecoratedTypeNameForTag(GetTypeTagInternal());
    168   }
    169   // Swaps the value of this object with that of |other|.
    170   void Swap(Any& other);
    171   // Checks if Any is empty, that is, not containing a value of any type.
    172   bool IsEmpty() const;
    173   // Clears the Any and destroys any contained object. Makes it empty.
    174   void Clear();
    175   // Checks if Any contains a type convertible to integer.
    176   // Any type that match std::is_integral<T> and std::is_enum<T> is accepted.
    177   // That includes signed and unsigned char, short, int, long, etc as well as
    178   // 'bool' and enumerated types.
    179   // For 'integer' type, you can call GetAsInteger to do implicit type
    180   // conversion to intmax_t.
    181   bool IsConvertibleToInteger() const;
    182   // For integral types and enums contained in the Any, get the integer value
    183   // of data. This is a useful function to obtain an integer value when
    184   // any can possibly have unspecified integer, such as 'short', 'unsigned long'
    185   // and so on.
    186   intmax_t GetAsInteger() const;
    187   // Writes the contained data to D-Bus message writer, if the appropriate
    188   // serialization method for contained data of the given type is provided
    189   // (an appropriate specialization of AppendValueToWriter<T>() is available).
    190   // Returns false if the Any is empty or if there is no serialization method
    191   // defined for the contained data.
    192   void AppendToDBusMessageWriter(dbus::MessageWriter* writer) const;
    193 
    194  private:
    195   // Returns a pointer to a static buffer containing type tag (sort of a type
    196   // name) of the contained value.
    197   const char* GetTypeTagInternal() const;
    198 
    199   // The data buffer for contained object.
    200   internal_details::Buffer data_buffer_;
    201 };
    202 
    203 }  // namespace brillo
    204 
    205 namespace std {
    206 
    207 // Specialize std::swap() algorithm for brillo::Any class.
    208 inline void swap(brillo::Any& lhs, brillo::Any& rhs) {
    209   lhs.Swap(rhs);
    210 }
    211 
    212 }  // namespace std
    213 
    214 #endif  // LIBBRILLO_BRILLO_ANY_H_
    215