-
Notifications
You must be signed in to change notification settings - Fork 333
/
boxed_value.hpp
358 lines (293 loc) · 12.1 KB
/
boxed_value.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
// This file is distributed under the BSD License.
// See "license.txt" for details.
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
// http://www.chaiscript.com
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#ifndef CHAISCRIPT_BOXED_VALUE_HPP_
#define CHAISCRIPT_BOXED_VALUE_HPP_
#include <map>
#include <memory>
#include <type_traits>
#include "../chaiscript_defines.hpp"
#include "any.hpp"
#include "type_info.hpp"
namespace chaiscript {
/// \brief A wrapper for holding any valid C++ type. All types in ChaiScript are Boxed_Value objects
/// \sa chaiscript::boxed_cast
class Boxed_Value {
public:
/// used for explicitly creating a "void" object
struct Void_Type {
};
private:
/// structure which holds the internal state of a Boxed_Value
/// \todo Get rid of Any and merge it with this, reducing an allocation in the process
struct Data {
Data(const Type_Info &ti, chaiscript::detail::Any to, bool is_ref, const void *t_void_ptr, bool t_return_value)
: m_type_info(ti)
, m_obj(std::move(to))
, m_data_ptr(ti.is_const() ? nullptr : const_cast<void *>(t_void_ptr))
, m_const_data_ptr(t_void_ptr)
, m_is_ref(is_ref)
, m_return_value(t_return_value) {
}
Data &operator=(const Data &rhs) {
m_type_info = rhs.m_type_info;
m_obj = rhs.m_obj;
m_is_ref = rhs.m_is_ref;
m_data_ptr = rhs.m_data_ptr;
m_const_data_ptr = rhs.m_const_data_ptr;
m_return_value = rhs.m_return_value;
if (rhs.m_attrs) {
m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>(*rhs.m_attrs);
}
return *this;
}
Data(const Data &) = delete;
Data(Data &&) = default;
Data &operator=(Data &&rhs) = default;
Type_Info m_type_info;
chaiscript::detail::Any m_obj;
void *m_data_ptr;
const void *m_const_data_ptr;
std::unique_ptr<std::map<std::string, std::shared_ptr<Data>>> m_attrs;
bool m_is_ref;
bool m_return_value;
};
struct Object_Data {
static auto get(Boxed_Value::Void_Type, bool t_return_value) {
return std::make_shared<Data>(detail::Get_Type_Info<void>::get(), chaiscript::detail::Any(), false, nullptr, t_return_value);
}
template<typename T>
static auto get(const std::shared_ptr<T> *obj, bool t_return_value) {
return get(*obj, t_return_value);
}
template<typename T>
static auto get(const std::shared_ptr<T> &obj, bool t_return_value) {
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(), chaiscript::detail::Any(obj), false, obj.get(), t_return_value);
}
template<typename T>
static auto get(std::shared_ptr<T> &&obj, bool t_return_value) {
auto ptr = obj.get();
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(), chaiscript::detail::Any(std::move(obj)), false, ptr, t_return_value);
}
template<typename T>
static auto get(T *t, bool t_return_value) {
return get(std::ref(*t), t_return_value);
}
template<typename T>
static auto get(const T *t, bool t_return_value) {
return get(std::cref(*t), t_return_value);
}
template<typename T>
static auto get(std::reference_wrapper<T> obj, bool t_return_value) {
auto p = &obj.get();
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(), chaiscript::detail::Any(std::move(obj)), true, p, t_return_value);
}
template<typename T>
static auto get(std::unique_ptr<T> &&obj, bool t_return_value) {
auto ptr = obj.get();
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(),
chaiscript::detail::Any(std::make_shared<std::unique_ptr<T>>(std::move(obj))),
true,
ptr,
t_return_value);
}
template<typename T>
static auto get(T t, bool t_return_value) {
auto p = std::make_shared<T>(std::move(t));
auto ptr = p.get();
return std::make_shared<Data>(detail::Get_Type_Info<T>::get(), chaiscript::detail::Any(std::move(p)), false, ptr, t_return_value);
}
static std::shared_ptr<Data> get() { return std::make_shared<Data>(Type_Info(), chaiscript::detail::Any(), false, nullptr, false); }
};
public:
/// Basic Boxed_Value constructor
template<typename T, typename = std::enable_if_t<!std::is_same_v<Boxed_Value, std::decay_t<T>>>>
explicit Boxed_Value(T &&t, bool t_return_value = false)
: m_data(Object_Data::get(std::forward<T>(t), t_return_value)) {
}
/// Unknown-type constructor
Boxed_Value() = default;
Boxed_Value(Boxed_Value &&) = default;
Boxed_Value &operator=(Boxed_Value &&) = default;
Boxed_Value(const Boxed_Value &) = default;
Boxed_Value &operator=(const Boxed_Value &) = default;
void swap(Boxed_Value &rhs) noexcept { std::swap(m_data, rhs.m_data); }
/// Copy the values stored in rhs.m_data to m_data.
/// m_data pointers are not shared in this case
Boxed_Value assign(const Boxed_Value &rhs) noexcept {
(*m_data) = (*rhs.m_data);
return *this;
}
const Type_Info &get_type_info() const noexcept { return m_data->m_type_info; }
/// return true if the object is uninitialized
bool is_undef() const noexcept { return m_data->m_type_info.is_undef(); }
bool is_const() const noexcept { return m_data->m_type_info.is_const(); }
bool is_type(const Type_Info &ti) const noexcept { return m_data->m_type_info.bare_equal(ti); }
template<typename T>
auto pointer_sentinel(std::shared_ptr<T> &ptr) const noexcept {
struct Sentinel {
Sentinel(std::shared_ptr<T> &t_ptr, Data &data)
: m_ptr(t_ptr)
, m_data(data) {
}
~Sentinel() {
// save new pointer data
const auto ptr_ = m_ptr.get().get();
m_data.get().m_data_ptr = ptr_;
m_data.get().m_const_data_ptr = ptr_;
}
Sentinel &operator=(Sentinel &&s) = default;
Sentinel(Sentinel &&s) = default;
operator std::shared_ptr<T> &() const noexcept { return m_ptr.get(); }
Sentinel &operator=(const Sentinel &) = delete;
Sentinel(Sentinel &) = delete;
std::reference_wrapper<std::shared_ptr<T>> m_ptr;
std::reference_wrapper<Data> m_data;
};
return Sentinel(ptr, *(m_data.get()));
}
bool is_null() const noexcept { return (m_data->m_data_ptr == nullptr && m_data->m_const_data_ptr == nullptr); }
const chaiscript::detail::Any &get() const noexcept { return m_data->m_obj; }
bool is_ref() const noexcept { return m_data->m_is_ref; }
bool is_return_value() const noexcept { return m_data->m_return_value; }
void reset_return_value() const noexcept { m_data->m_return_value = false; }
bool is_pointer() const noexcept { return !is_ref(); }
void *get_ptr() const noexcept { return m_data->m_data_ptr; }
const void *get_const_ptr() const noexcept { return m_data->m_const_data_ptr; }
Boxed_Value get_attr(const std::string &t_name) {
if (!m_data->m_attrs) {
m_data->m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>();
}
auto &attr = (*m_data->m_attrs)[t_name];
if (attr) {
return Boxed_Value(attr, Internal_Construction());
} else {
Boxed_Value bv; // default construct a new one
attr = bv.m_data;
return bv;
}
}
Boxed_Value ©_attrs(const Boxed_Value &t_obj) {
if (t_obj.m_data->m_attrs) {
m_data->m_attrs = std::make_unique<std::map<std::string, std::shared_ptr<Data>>>(*t_obj.m_data->m_attrs);
}
return *this;
}
Boxed_Value &clone_attrs(const Boxed_Value &t_obj) {
copy_attrs(t_obj);
reset_return_value();
return *this;
}
/// \returns true if the two Boxed_Values share the same internal type
static bool type_match(const Boxed_Value &l, const Boxed_Value &r) noexcept { return l.get_type_info() == r.get_type_info(); }
private:
// necessary to avoid hitting the templated && constructor of Boxed_Value
struct Internal_Construction {
};
Boxed_Value(std::shared_ptr<Data> t_data, Internal_Construction)
: m_data(std::move(t_data)) {
}
std::shared_ptr<Data> m_data = Object_Data::get();
};
/// @brief Creates a Boxed_Value. If the object passed in is a value type, it is copied. If it is a pointer, std::shared_ptr, or
/// std::reference_type
/// a copy is not made.
/// @param t The value to box
///
/// Example:
///
/// ~~~{.cpp}
/// int i;
/// chaiscript::ChaiScript chai;
/// chai.add(chaiscript::var(i), "i");
/// chai.add(chaiscript::var(&i), "ip");
/// ~~~
///
/// @sa @ref adding_objects
template<typename T>
Boxed_Value var(T &&t) {
return Boxed_Value(std::forward<T>(t));
}
namespace detail {
/// \brief Takes a value, copies it and returns a Boxed_Value object that is immutable
/// \param[in] t Value to copy and make const
/// \returns Immutable Boxed_Value
/// \sa Boxed_Value::is_const
template<typename T>
Boxed_Value const_var_impl(const T &t) {
return Boxed_Value(std::make_shared<typename std::add_const<T>::type>(t));
}
/// \brief Takes a pointer to a value, adds const to the pointed to type and returns an immutable Boxed_Value.
/// Does not copy the pointed to value.
/// \param[in] t Pointer to make immutable
/// \returns Immutable Boxed_Value
/// \sa Boxed_Value::is_const
template<typename T>
Boxed_Value const_var_impl(T *t) {
return Boxed_Value(const_cast<typename std::add_const<T>::type *>(t));
}
/// \brief Takes a std::shared_ptr to a value, adds const to the pointed to type and returns an immutable Boxed_Value.
/// Does not copy the pointed to value.
/// \param[in] t Pointer to make immutable
/// \returns Immutable Boxed_Value
/// \sa Boxed_Value::is_const
template<typename T>
Boxed_Value const_var_impl(const std::shared_ptr<T> &t) {
return Boxed_Value(std::const_pointer_cast<typename std::add_const<T>::type>(t));
}
/// \brief Takes a std::reference_wrapper value, adds const to the referenced type and returns an immutable Boxed_Value.
/// Does not copy the referenced value.
/// \param[in] t Reference object to make immutable
/// \returns Immutable Boxed_Value
/// \sa Boxed_Value::is_const
template<typename T>
Boxed_Value const_var_impl(const std::reference_wrapper<T> &t) {
return Boxed_Value(std::cref(t.get()));
}
} // namespace detail
/// \brief Takes an object and returns an immutable Boxed_Value. If the object is a std::reference or pointer type
/// the value is not copied. If it is an object type, it is copied.
/// \param[in] t Object to make immutable
/// \returns Immutable Boxed_Value
/// \sa chaiscript::Boxed_Value::is_const
/// \sa chaiscript::var
///
/// Example:
/// \code
/// enum Colors
/// {
/// Blue,
/// Green,
/// Red
/// };
/// chaiscript::ChaiScript chai
/// chai.add(chaiscript::const_var(Blue), "Blue"); // add immutable constant
/// chai.add(chaiscript::const_var(Red), "Red");
/// chai.add(chaiscript::const_var(Green), "Green");
/// \endcode
///
/// \todo support C++11 strongly typed enums
/// \sa \ref adding_objects
template<typename T>
Boxed_Value const_var(const T &t) {
return detail::const_var_impl(t);
}
inline Boxed_Value void_var() {
static const auto v = Boxed_Value(Boxed_Value::Void_Type());
return v;
}
inline Boxed_Value const_var(bool b) {
static const auto t = detail::const_var_impl(true);
static const auto f = detail::const_var_impl(false);
if (b) {
return t;
} else {
return f;
}
}
} // namespace chaiscript
#endif