blob: 68ff996294a801348636d4f7ada271b141c922ce [file] [log] [blame]
Alex Vakulenko9205c772014-08-22 15:05:35 -07001// 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 chromeos::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// chromeos::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 LIBCHROMEOS_CHROMEOS_ANY_H_
24#define LIBCHROMEOS_CHROMEOS_ANY_H_
25
26#include <chromeos/any_internal_impl.h>
27
28#include <algorithm>
29
Alex Vakulenko847b8712014-08-29 10:43:06 -070030#include <chromeos/chromeos_export.h>
Alex Vakulenkobba50fa2014-10-03 08:37:54 -070031#include <chromeos/type_name_undecorate.h>
Alex Vakulenko847b8712014-08-29 10:43:06 -070032
Alex Vakulenko7c294a22014-08-23 19:31:27 -070033namespace dbus {
34class MessageWriter;
35} // namespace dbus
36
Alex Vakulenko9205c772014-08-22 15:05:35 -070037namespace chromeos {
38
Alex Vakulenko847b8712014-08-29 10:43:06 -070039class CHROMEOS_EXPORT Any final {
Alex Vakulenko9205c772014-08-22 15:05:35 -070040 public:
Alex Vakulenko847b8712014-08-29 10:43:06 -070041 Any(); // Do not inline to hide internal_details::Buffer from export table.
Alex Vakulenko9205c772014-08-22 15:05:35 -070042 // 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>
Alex Vakulenko847b8712014-08-29 10:43:06 -070049 inline Any(T value) { // NOLINT(runtime/explicit)
Alex Vakulenko9205c772014-08-22 15:05:35 -070050 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>
Alex Vakulenko847b8712014-08-29 10:43:06 -070061 inline Any& operator=(T value) {
Alex Vakulenko9205c772014-08-22 15:05:35 -070062 data_buffer_.Assign(std::move(value));
63 return *this;
64 }
65
Alex Vakulenko88c1b4b2014-09-03 15:16:10 -070066 // 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
Alex Vakulenko9205c772014-08-22 15:05:35 -070073 // 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 std::type_info& ContainedTypeId = GetType();
87 if (typeid(CanonicalDestType) == ContainedTypeId)
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 using CanonicalDestTypeNoVolatile = typename std::add_pointer<
101 typename std::remove_volatile<NonPointer>::type>::type;
102 using CanonicalDestTypeNoConstOrVolatile = typename std::add_pointer<
103 typename std::remove_cv<NonPointer>::type>::type;
104
105 return typeid(CanonicalDestTypeNoConst) == ContainedTypeId ||
106 typeid(CanonicalDestTypeNoVolatile) == ContainedTypeId ||
107 typeid(CanonicalDestTypeNoConstOrVolatile) == ContainedTypeId;
108 }
109
110 // Returns immutable data contained in Any.
111 // Aborts if Any doesn't contain a value of type T, or trivially
112 // convertible to/compatible with it.
113 template<typename T>
114 const T& Get() const {
Alex Vakulenko05d29042015-01-13 09:39:25 -0800115 CHECK(IsTypeCompatible<T>())
116 << "Requesting value of type '" << GetUndecoratedTypeName<T>()
117 << "' from variant containing '" << UndecorateTypeName(GetType().name())
118 << "'";
Alex Vakulenko9205c772014-08-22 15:05:35 -0700119 return data_buffer_.GetData<T>();
120 }
121
Christopher Wileydfef2362015-04-02 09:10:31 -0700122 // Returns a copy of data in Any and returns true when that data is
123 // compatible with T. Returns false if contained data is incompatible.
124 template<typename T>
125 bool GetValue(T* value) const {
126 if (!IsTypeCompatible<T>()) {
127 return false;
128 }
129 *value = Get<T>();
130 return true;
131 }
132
Alex Vakulenko9205c772014-08-22 15:05:35 -0700133 // Returns a pointer to mutable value of type T contained within Any.
134 // No data copying is made, the data pointed to is still owned by Any.
135 // If Any doesn't contain a value of type T, or trivially
136 // convertible/compatible to/with it, then it returns nullptr.
137 template<typename T>
138 T* GetPtr() {
139 if (!IsTypeCompatible<T>())
140 return nullptr;
141 return &(data_buffer_.GetData<T>());
142 }
143
Alex Vakulenkof3cf5432014-09-19 14:44:40 -0700144 // Returns a copy of the data contained in Any.
Alex Vakulenko9205c772014-08-22 15:05:35 -0700145 // If the Any doesn't contain a compatible value, the provided default
146 // |def_val| is returned instead.
147 template<typename T>
Alex Vakulenkof3cf5432014-09-19 14:44:40 -0700148 T TryGet(typename std::decay<T>::type const& def_val) const {
Alex Vakulenko9205c772014-08-22 15:05:35 -0700149 if (!IsTypeCompatible<T>())
150 return def_val;
151 return data_buffer_.GetData<T>();
152 }
153
154 // A convenience specialization of the above function where the default
155 // value of type T is returned in case the underlying Get() fails.
156 template<typename T>
Alex Vakulenkof3cf5432014-09-19 14:44:40 -0700157 T TryGet() const {
Alex Vakulenko9205c772014-08-22 15:05:35 -0700158 return TryGet<T>(typename std::decay<T>::type());
159 }
160
Alex Vakulenko9205c772014-08-22 15:05:35 -0700161 // Returns the type information about the contained data. For most cases,
162 // instead of using this function, you should be calling IsTypeCompatible<>().
163 const std::type_info& GetType() const;
164 // Swaps the value of this object with that of |other|.
165 void Swap(Any& other);
166 // Checks if Any is empty, that is, not containing a value of any type.
167 bool IsEmpty() const;
168 // Clears the Any and destroys any contained object. Makes it empty.
169 void Clear();
170 // Checks if Any contains a type convertible to integer.
171 // Any type that match std::is_integral<T> and std::is_enum<T> is accepted.
172 // That includes signed and unsigned char, short, int, long, etc as well as
173 // 'bool' and enumerated types.
174 // For 'integer' type, you can call GetAsInteger to do implicit type
175 // conversion to intmax_t.
176 bool IsConvertibleToInteger() const;
177 // For integral types and enums contained in the Any, get the integer value
178 // of data. This is a useful function to obtain an integer value when
179 // any can possibly have unspecified integer, such as 'short', 'unsigned long'
180 // and so on.
181 intmax_t GetAsInteger() const;
Alex Vakulenko7c294a22014-08-23 19:31:27 -0700182 // Writes the contained data to D-Bus message writer, if the appropriate
183 // serialization method for contained data of the given type is provided
184 // (an appropriate specialization of AppendValueToWriter<T>() is available).
185 // Returns false if the Any is empty or if there is no serialization method
186 // defined for the contained data.
Alex Vakulenko003e3bb2014-11-09 13:21:59 -0800187 void AppendToDBusMessageWriter(dbus::MessageWriter* writer) const;
Alex Vakulenko9205c772014-08-22 15:05:35 -0700188
189 private:
190 // The data buffer for contained object.
191 internal_details::Buffer data_buffer_;
192};
193
194} // namespace chromeos
195
196namespace std {
197
198// Specialize std::swap() algorithm for chromeos::Any class.
199inline void swap(chromeos::Any& lhs, chromeos::Any& rhs) {
200 lhs.Swap(rhs);
201}
202
203} // namespace std
204
205#endif // LIBCHROMEOS_CHROMEOS_ANY_H_