boxed_value.hpp 14 KB

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