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