|  | // Copyright 2014 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. | 
|  |  | 
|  | // This is an implementation of a "true" variant class in C++. | 
|  | // The buffet::Any class can hold any C++ type, but both the setter and | 
|  | // getter sites need to know the actual type of data. | 
|  | // Note that C-style arrays when stored in Any are reduced to simple | 
|  | // data pointers. Any will not copy a contents of the array. | 
|  | //    const int data[] = [1,2,3]; | 
|  | //    Any v(data);  // stores const int*, effectively "Any v(&data[0]);" | 
|  |  | 
|  | // buffet::Any is a value type. Which means, the data is copied into it | 
|  | // and Any owns it. The owned object (stored by value) will be destroyed | 
|  | // when Any is cleared or reassigned. The contained value type must be | 
|  | // copy-constructible. You can also store pointers and references to objects. | 
|  | // Storing pointers is trivial. In order to store a reference, you can | 
|  | // use helper functions std::ref() and std::cref() to create non-const and | 
|  | // const references respectively. In such a case, the type of contained data | 
|  | // will be std::reference_wrapper<T>. See 'References' unit tests in | 
|  | // any_unittest.cc for examples. | 
|  |  | 
|  | #ifndef BUFFET_ANY_H_ | 
|  | #define BUFFET_ANY_H_ | 
|  |  | 
|  | #include "buffet/any_internal_impl.h" | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | namespace buffet { | 
|  |  | 
|  | class Any final { | 
|  | public: | 
|  | Any() = default; | 
|  | // Standard copy constructor. This is a value-class container | 
|  | // that must be copy-constructible and movable. The copy constructors | 
|  | // should not be marked as explicit. | 
|  | Any(const Any& rhs); | 
|  | // Typed constructor that stores a value of type T in the Any. | 
|  | template<class T> | 
|  | Any(T value) {        // NOLINT(runtime/explicit) | 
|  | data_buffer_.Assign(std::move(value)); | 
|  | } | 
|  |  | 
|  | // Not declaring the destructor as virtual since this is a sealed class | 
|  | // and there is no need to introduce a virtual table to it. | 
|  | ~Any(); | 
|  |  | 
|  | // Assignment operators. | 
|  | Any& operator=(const Any& rhs); | 
|  | template<class T> | 
|  | Any& operator=(T value) { | 
|  | data_buffer_.Assign(std::move(value)); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | // Checks if the given type DestType can be obtained from the Any. | 
|  | // For example, to check if Any has a 'double' value in it: | 
|  | //  any.IsTypeCompatible<double>() | 
|  | template<typename DestType> | 
|  | bool IsTypeCompatible() const { | 
|  | // Make sure the requested type DestType conforms to the storage | 
|  | // requirements of Any. We always store the data by value, which means we | 
|  | // strip away any references as well as cv-qualifiers. So, if the user | 
|  | // stores "const int&", we actually store just an "int". | 
|  | // When calling IsTypeCompatible, we need to do a similar "type cleansing" | 
|  | // to make sure the requested type matches the type of data actually stored, | 
|  | // so this "canonical" type is used for type checking below. | 
|  | using CanonicalDestType = typename std::decay<DestType>::type; | 
|  | const std::type_info& ContainedTypeId = GetType(); | 
|  | if (typeid(CanonicalDestType) == ContainedTypeId) | 
|  | return true; | 
|  |  | 
|  | if (!std::is_pointer<CanonicalDestType>::value) | 
|  | return false; | 
|  |  | 
|  | // If asking for a const pointer from a variant containing non-const | 
|  | // pointer, still satisfy the request. So, we need to remove the pointer | 
|  | // specification first, then strip the const/volatile qualifiers, then | 
|  | // re-add the pointer back, so "const int*" would become "int*". | 
|  | using NonPointer = typename std::remove_pointer<CanonicalDestType>::type; | 
|  | using CanonicalDestTypeNoConst = typename std::add_pointer< | 
|  | typename std::remove_const<NonPointer>::type>::type; | 
|  | using CanonicalDestTypeNoVolatile = typename std::add_pointer< | 
|  | typename std::remove_volatile<NonPointer>::type>::type; | 
|  | using CanonicalDestTypeNoConstOrVolatile = typename std::add_pointer< | 
|  | typename std::remove_cv<NonPointer>::type>::type; | 
|  |  | 
|  | return typeid(CanonicalDestTypeNoConst) == ContainedTypeId || | 
|  | typeid(CanonicalDestTypeNoVolatile) == ContainedTypeId || | 
|  | typeid(CanonicalDestTypeNoConstOrVolatile) == ContainedTypeId; | 
|  | } | 
|  |  | 
|  | // Returns immutable data contained in Any. | 
|  | // Aborts if Any doesn't contain a value of type T, or trivially | 
|  | // convertible to/compatible with it. | 
|  | template<typename T> | 
|  | const T& Get() const { | 
|  | CHECK(IsTypeCompatible<T>()) << "Requesting value of type " | 
|  | << typeid(T).name() | 
|  | << " from variant containing " | 
|  | << GetType().name(); | 
|  | return data_buffer_.GetData<T>(); | 
|  | } | 
|  |  | 
|  | // Returns a pointer to mutable value of type T contained within Any. | 
|  | // No data copying is made, the data pointed to is still owned by Any. | 
|  | // If Any doesn't contain a value of type T, or trivially | 
|  | // convertible/compatible to/with it, then it returns nullptr. | 
|  | template<typename T> | 
|  | T* GetPtr() { | 
|  | if (!IsTypeCompatible<T>()) | 
|  | return nullptr; | 
|  | return &(data_buffer_.GetData<T>()); | 
|  | } | 
|  |  | 
|  | // Returns immutable data contained in Any. | 
|  | // If the Any doesn't contain a compatible value, the provided default | 
|  | // |def_val| is returned instead. | 
|  | template<typename T> | 
|  | const T& TryGet(typename std::decay<T>::type const& def_val) const { | 
|  | if (!IsTypeCompatible<T>()) | 
|  | return def_val; | 
|  | return data_buffer_.GetData<T>(); | 
|  | } | 
|  |  | 
|  | // A convenience specialization of the above function where the default | 
|  | // value of type T is returned in case the underlying Get() fails. | 
|  | template<typename T> | 
|  | const T& TryGet() const { | 
|  | return TryGet<T>(typename std::decay<T>::type()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Returns the type information about the contained data. For most cases, | 
|  | // instead of using this function, you should be calling IsTypeCompatible<>(). | 
|  | const std::type_info& GetType() const; | 
|  | // Swaps the value of this object with that of |other|. | 
|  | void Swap(Any& other); | 
|  | // Checks if Any is empty, that is, not containing a value of any type. | 
|  | bool IsEmpty() const; | 
|  | // Clears the Any and destroys any contained object. Makes it empty. | 
|  | void Clear(); | 
|  | // Checks if Any contains a type convertible to integer. | 
|  | // Any type that match std::is_integral<T> and std::is_enum<T> is accepted. | 
|  | // That includes signed and unsigned char, short, int, long, etc as well as | 
|  | // 'bool' and enumerated types. | 
|  | // For 'integer' type, you can call GetAsInteger to do implicit type | 
|  | // conversion to intmax_t. | 
|  | bool IsConvertibleToInteger() const; | 
|  | // For integral types and enums contained in the Any, get the integer value | 
|  | // of data. This is a useful function to obtain an integer value when | 
|  | // any can possibly have unspecified integer, such as 'short', 'unsigned long' | 
|  | // and so on. | 
|  | intmax_t GetAsInteger() const; | 
|  |  | 
|  | private: | 
|  | // The data buffer for contained object. | 
|  | internal_details::Buffer data_buffer_; | 
|  | }; | 
|  |  | 
|  | }  // namespace buffet | 
|  |  | 
|  | namespace std { | 
|  |  | 
|  | // Specialize std::swap() algorithm for buffet::Any class. | 
|  | inline void swap(buffet::Any& lhs, buffet::Any& rhs) { | 
|  | lhs.Swap(rhs); | 
|  | } | 
|  |  | 
|  | }  // namespace std | 
|  |  | 
|  | #endif  // BUFFET_ANY_H_ | 
|  |  |