blob: 5ab249cd13b0f281d41c07914ed0f0f5247724cd [file] [log] [blame]
Alex Vakulenkob6513a12014-05-05 17:23:40 -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 buffet::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// buffet::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 BUFFET_ANY_H_
24#define BUFFET_ANY_H_
25
26#include "buffet/any_internal_impl.h"
27
28#include <algorithm>
29
30namespace buffet {
31
32class Any final {
33 public:
34 Any() = default;
35 // Standard copy constructor. This is a value-class container
36 // that must be copy-constructible and movable. The copy constructors
37 // should not be marked as explicit.
38 Any(const Any& rhs);
39 // Typed constructor that stores a value of type T in the Any.
40 template<class T>
41 Any(T value) { // NOLINT(runtime/explicit)
42 data_buffer_.Assign(std::move(value));
43 }
44
45 // Not declaring the destructor as virtual since this is a sealed class
46 // and there is no need to introduce a virtual table to it.
47 ~Any();
48
49 // Assignment operators.
50 Any& operator=(const Any& rhs);
51 template<class T>
52 Any& operator=(T value) {
53 data_buffer_.Assign(std::move(value));
54 return *this;
55 }
56
57 // Checks if the given type DestType can be obtained from the Any.
58 // For example, to check if Any has a 'double' value in it:
59 // any.IsTypeCompatible<double>()
60 template<typename DestType>
61 bool IsTypeCompatible() const {
62 // Make sure the requested type DestType conforms to the storage
63 // requirements of Any. We always store the data by value, which means we
64 // strip away any references as well as cv-qualifiers. So, if the user
65 // stores "const int&", we actually store just an "int".
66 // When calling IsTypeCompatible, we need to do a similar "type cleansing"
67 // to make sure the requested type matches the type of data actually stored,
68 // so this "canonical" type is used for type checking below.
69 using CanonicalDestType = typename std::decay<DestType>::type;
70 const std::type_info& ContainedTypeId = GetType();
71 if (typeid(CanonicalDestType) == ContainedTypeId)
72 return true;
73
74 if (!std::is_pointer<CanonicalDestType>::value)
75 return false;
76
77 // If asking for a const pointer from a variant containing non-const
78 // pointer, still satisfy the request. So, we need to remove the pointer
79 // specification first, then strip the const/volatile qualifiers, then
80 // re-add the pointer back, so "const int*" would become "int*".
81 using NonPointer = typename std::remove_pointer<CanonicalDestType>::type;
82 using CanonicalDestTypeNoConst = typename std::add_pointer<
83 typename std::remove_const<NonPointer>::type>::type;
84 using CanonicalDestTypeNoVolatile = typename std::add_pointer<
85 typename std::remove_volatile<NonPointer>::type>::type;
86 using CanonicalDestTypeNoConstOrVolatile = typename std::add_pointer<
87 typename std::remove_cv<NonPointer>::type>::type;
88
89 return typeid(CanonicalDestTypeNoConst) == ContainedTypeId ||
90 typeid(CanonicalDestTypeNoVolatile) == ContainedTypeId ||
91 typeid(CanonicalDestTypeNoConstOrVolatile) == ContainedTypeId;
92 }
93
94 // Returns immutable data contained in Any.
95 // Aborts if Any doesn't contain a value of type T, or trivially
96 // convertible to/compatible with it.
97 template<typename T>
98 const T& Get() const {
99 CHECK(IsTypeCompatible<T>()) << "Requesting value of type "
100 << typeid(T).name()
101 << " from variant containing "
102 << GetType().name();
103 return data_buffer_.GetData<T>();
104 }
105
106 // Returns a pointer to mutable value of type T contained within Any.
107 // No data copying is made, the data pointed to is still owned by Any.
108 // If Any doesn't contain a value of type T, or trivially
109 // convertible/compatible to/with it, then it returns nullptr.
110 template<typename T>
111 T* GetPtr() {
112 if (!IsTypeCompatible<T>())
113 return nullptr;
114 return &(data_buffer_.GetData<T>());
115 }
116
117 // Returns immutable data contained in Any.
118 // If the Any doesn't contain a compatible value, the provided default
119 // |def_val| is returned instead.
120 template<typename T>
121 const T& TryGet(typename std::decay<T>::type const& def_val) const {
122 if (!IsTypeCompatible<T>())
123 return def_val;
124 return data_buffer_.GetData<T>();
125 }
126
127 // A convenience specialization of the above function where the default
128 // value of type T is returned in case the underlying Get() fails.
129 template<typename T>
130 const T& TryGet() const {
131 return TryGet<T>(typename std::decay<T>::type());
132 }
133
134
135 // Returns the type information about the contained data. For most cases,
136 // instead of using this function, you should be calling IsTypeCompatible<>().
137 const std::type_info& GetType() const;
138 // Swaps the value of this object with that of |other|.
139 void Swap(Any& other);
140 // Checks if Any is empty, that is, not containing a value of any type.
141 bool IsEmpty() const;
142 // Clears the Any and destroys any contained object. Makes it empty.
143 void Clear();
144 // Checks if Any contains a type convertible to integer.
145 // Any type that match std::is_integral<T> and std::is_enum<T> is accepted.
146 // That includes signed and unsigned char, short, int, long, etc as well as
147 // 'bool' and enumerated types.
148 // For 'integer' type, you can call GetAsInteger to do implicit type
149 // conversion to intmax_t.
150 bool IsConvertibleToInteger() const;
151 // For integral types and enums contained in the Any, get the integer value
152 // of data. This is a useful function to obtain an integer value when
153 // any can possibly have unspecified integer, such as 'short', 'unsigned long'
154 // and so on.
155 intmax_t GetAsInteger() const;
156
157 private:
158 // The data buffer for contained object.
159 internal_details::Buffer data_buffer_;
160};
161
162} // namespace buffet
163
164namespace std {
165
166// Specialize std::swap() algorithm for buffet::Any class.
167inline void swap(buffet::Any& lhs, buffet::Any& rhs) {
168 lhs.Swap(rhs);
169}
170
171} // namespace std
172
173#endif // BUFFET_ANY_H_
174