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