Turi Create  4.0
any.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 // Modified from boost 1.37 boost::any
7 // Extended to handle turicreate serialization/deserialization functions
8 // See http://www.boost.org/libs/any for Documentation.
9 
10 #ifndef TURI_ANY_INCLUDED
11 #define TURI_ANY_INCLUDED
12 
13 // what: variant type boost::any
14 // who: contributed by Kevlin Henney,
15 // with features contributed and bugs found by
16 // Ed Brey, Mark Rodgers, Peter Dimov, and James Curran
17 // when: July 2001
18 // where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95
19 
20 #include <algorithm>
21 #include <typeinfo>
22 #include <map>
23 #include <iostream>
24 #include <stdint.h>
25 
26 #include <boost/type_traits/remove_reference.hpp>
27 #include <boost/type_traits/is_reference.hpp>
28 #include <boost/throw_exception.hpp>
29 #include <boost/static_assert.hpp>
30 #include <boost/utility.hpp>
31 #include <boost/exception/detail/is_output_streamable.hpp>
32 #include <boost/functional/hash.hpp>
33 
34 
35 #include <core/logging/assertions.hpp>
36 #include <core/storage/serialization/serialization_includes.hpp>
37 
38 namespace turi {
39  /**
40  * \ingroup util
41  A generic "variant" object obtained from Boost::Any and modified to
42  be serializable. A variable of type "any" can store any datatype
43  (even dynamically changeable at runtime), but the caveat is that
44  you must know the exact stored type to be able to extract the data
45  safely.
46 
47  To serialize/deserialize the any, regular serialization procedures
48  apply. However, since a statically initialized type registration
49  system is used to identify the type of the deserialized object, so
50  the user must pay attention to a couple of minor issues.
51 
52  On serialization:
53 
54  \li \b a) If an any contains a serializable type, the any can be
55  serialized.
56  \li \b b) If an any contains an unserializable type, the
57  serialization will fail at runtime.
58 
59  On deserialization:
60 
61  \li \b c) An empty any can be constructed with no type information
62  and it can be deserialized from an archive.
63  \li \b d) However, the deserialization will fail at runtime if the
64  true type of the any is never accessed / instantiated
65  anywhere in the code.
66 
67  Condition \b d) is particular unusual so I will illustrate with an
68  example.
69 
70  Given a simple user struct:
71  \code
72  struct UserStruct {
73  int i;
74  void save (turi::oarchive& oarc) const {
75  oarc << i;
76  }
77  void load (turi::iarchive& iarc) {
78  iarc << i;
79  }
80  }
81  \endcode
82 
83  If an any object contains the struct, it will be serializable.
84 
85  \code
86  UserStruct us;
87  us.i = 10;
88  any a = us;
89  // output file
90  std::ofstream fout("test.bin");
91  turi::oarchive oarc(fout);
92  oarc << a; // write the any
93  \endcode
94 
95  To deserialize, I will open an input archive and stream into an any.
96 
97  \code
98  // open input
99  std::ifstream fin("test.bin");
100  turi::iarchive iarc(fin);
101  // create an any and read it
102  any a;
103  iarc >> a;
104  \endcode
105 
106  Now, unusually, the above code will fail, while the following code
107  will succeed
108 
109  \code
110  // open input
111  std::ifstream fin("test.bin");
112  turi::iarchive iarc(fin);
113  // create an any and read it
114  any a;
115  iarc >> a;
116  std::cout << a.as<UserStruct>().i;
117  \endcode
118 
119  The <tt> a.as<UserStruct>() </tt> forces the instantiation of static functions
120  which allow the any deserialization to identify the UserStruct type.
121  */
122  class any {
123  private:
124  /**
125  * iholder is the base abstract type used to store the contents
126  */
127  class iholder {
128  public: // structors
129  virtual ~iholder() { }
130  virtual const std::type_info& type() const = 0;
131  virtual iholder * clone() const = 0;
132  virtual uint64_t deserializer_id() const = 0;
133  virtual void deep_op_equal(const iholder* c) = 0;
134  static iholder* load(iarchive_soft_fail &arc);
135  virtual void save(oarchive_soft_fail& arc) const = 0;
136  virtual std::ostream& print(std::ostream& out) const = 0;
137  };
138  iholder* contents;
139 
140  public: // structors
141  /// default constructor. Creates an empty any
142  any() : contents(NULL) { }
143 
144  /// Creates an any which stores the value
145  template<typename ValueType>
146  explicit any(const ValueType& value)
147  : contents(new holder<ValueType>(value)) { }
148 
149  /// Construct an any from another any
150  any(const any & other) :
151  contents(other.empty() ? NULL : other.contents->clone()) { }
152 
153  /// Destroy the contentss of this any
154  ~any() { delete contents; }
155 
156  /// Returns true if the object does not contain any stored data
157  bool empty() const { return contents == NULL; }
158 
159  /// Extracts a reference to the contents of the any as a type of
160  /// ValueType
161  template<typename ValueType>
162  ValueType& as() {
163  DASSERT_TRUE(type() == typeid(ValueType));
164  DASSERT_FALSE(empty());
165  return static_cast<holder<ValueType> *>(contents)->contents;
166  }
167 
168  /// Extracts a constant reference to the contents of the any as a
169  /// type of ValueType
170  template<typename ValueType>
171  inline const ValueType& as() const{
172  DASSERT_TRUE(type() == typeid(ValueType));
173  DASSERT_FALSE(empty());
174  return static_cast< holder<ValueType> *>(contents)->contents;
175  }
176 
177  /// Returns true if the contained type is of type ValueType
178  template<typename ValueType>
179  bool is() const{
180  return (type() == typeid(ValueType));
181  }
182 
183 
184  /// Exchanges the contents of two any's
185  any& swap(any & rhs) {
186  std::swap(contents, rhs.contents);
187  return *this;
188  }
189 
190  /**
191  * Update the contents of this any. If a new type is used than
192  * the type of this any will change.
193  */
194  template<typename ValueType>
195  any& operator=(const ValueType & rhs) {
196  if (contents != NULL && contents->type() == typeid(ValueType)) {
197  as<ValueType>() = rhs;
198  } else { any(rhs).swap(*this); }
199  return *this;
200  }
201 
202  /**
203  * Update the contents of this any to match the type of the other
204  * any.
205  */
206  any& operator=(const any & rhs) {
207  if (rhs.empty()) {
208  if (contents) delete contents;
209  contents = NULL;
210  } else {
211  if (contents != NULL && contents->type() == rhs.contents->type()) {
212  contents->deep_op_equal(rhs.contents);
213  } else { any(rhs).swap(*this); }
214  }
215  return *this;
216  }
217 
218  std::ostream& print(std::ostream& out) const {
219  return empty()? (out << "EMPTY") : contents->print(out);
220  }
221 
222  /// Returns the type information of the stored data.
223  const std::type_info& type() const {
224  return empty() ? typeid(void) : contents->type();
225  }
226 
227  /// Return the name of the internal type as a string.
228  const std::string type_name() const {
229  return empty() ? "NULL" : std::string(contents->type().name());
230  }
231 
232  /// loads the any from a file.
233  void load(iarchive& arc) {
234  iarchive_soft_fail isoftarc(arc);
235  if(contents != NULL) { delete contents; contents = NULL; }
236  bool isempty(true);
237  isoftarc >> isempty;
238  if (isempty == false) contents = iholder::load(isoftarc);
239  }
240 
241  /// Saves the any to a file. Caveats apply. See the main any docs.
242  void save(oarchive& arc) const {
243  oarchive_soft_fail osoftarc(arc);
244  bool isempty = empty();
245  osoftarc << isempty;
246  if (isempty == false) contents->save(osoftarc);
247  }
248 
249 
250  public:
251  /**
252  * This section contain the global registry used to determine the
253  * deserialization code for a particular type. Essentially the
254  * registry is a global map in which all subtypes of iholder
255  * register a deserialization function with their type.
256  */
257 
258  typedef iholder* (*deserialize_function_type)(iarchive_soft_fail& arc);
259  typedef std::map<uint64_t, deserialize_function_type> registry_map_type;
260  /**
261  * The get registry routine is a static method that gets a
262  * reference to the global registry. It is very important that
263  * this be a static method and not a static member to ensure that
264  * the global registry is defined before each holders try to
265  * register. This is accomplished by having get_registry
266  * statically declare the global registry
267  */
268  static registry_map_type& get_global_registry();
269 
270  public:
271 
272  template <typename ValueType> static
273  typename boost::disable_if_c<boost::is_output_streamable<ValueType>::value,
274  void>::type
275  print_type_or_contents(std::ostream& out, const ValueType &h) {
276  out << "Not_Printable[" << typeid(ValueType).name() << ']';
277  }
278 
279  template <typename ValueType> static
280  typename boost::enable_if_c<boost::is_output_streamable<ValueType>::value,
281  void>::type
282  print_type_or_contents(std::ostream& out, const ValueType &h) { out << h; }
283 
284 
285  public:
286 
287  /**
288  * holder is an instantiation of iholder
289  */
290  template<typename ValueType>
291  class holder : public iholder {
292  public:
293  typedef ValueType value_type;
294  /// The actual contents of the holder
295  ValueType contents;
296  /// Construct a holder from a value
297  holder(const ValueType& value) : contents(value) { }
298  /// Construct a holder from an archive
299  holder(iarchive_soft_fail& arc) { arc >> contents; }
300  /// Get the type info of the holder
301  const std::type_info& type() const { return typeid(ValueType); }
302  /// Clone a holder
303  iholder* clone() const { return new holder(contents); }
304  /// Deep assignment
305  void deep_op_equal(const iholder* other) {
306  contents = static_cast< const holder<ValueType>* >(other)->contents;
307  }
308  /**
309  * Get the deserializer id from the static registry associated
310  * with this type of holder
311  */
312  uint64_t deserializer_id() const { return registry.localid; }
313  void save(oarchive_soft_fail &arc) const {
314  arc << registry.localid << contents;
315  }
316  /**
317  * Print the contents or the type if the contents does not
318  * support printing
319  */
320  std::ostream& print(std::ostream& out) const {
321  any::print_type_or_contents(out, contents);
322  return out;
323  }
324  /** The actual deserialization function for this holder type */
325  static iholder* deserialize(iarchive_soft_fail &arc) {
326  return new holder(arc);
327  }
328  /**
329  * The following struct defines the static member used to
330  * automatically register the deserialization function for this
331  * holder type and cache a shared id used to quickly identify
332  * the deserialization function.
333  *
334  * Note that the registry actually uses the NAME of the type so
335  * renaming a type will result in an incompatible
336  * deserialization.
337  */
338  struct registry_type {
339  uint64_t localid;
340  registry_type() {
341  boost::hash<std::string> hash_function;
342  // compute localid
343  localid = hash_function(typeid(ValueType).name());
345  }
346  }; // end of registry type
347  /**
348  * The registry is a static member that will get constructed
349  * before main and used to register the any type
350  */
352  private:
353  holder& operator=(const holder& other) { }
354  }; // end of class holder
355 
356  }; // end of class any
357 
358 
359  /**
360  * This static membery computes the holder (type specific)
361  * deserialization id and also registers it with the global
362  * registry.
363  */
364  template<typename ValueType>
366 
367 } // namespace turi
368 
369 std::ostream& operator<<(std::ostream& out, const turi::any& any);
370 
371 
372 // Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
373 //
374 // Distributed under the Boost Software License, Version 1.0. (See
375 // accompanying file LICENSE_1_0.txt or copy at
376 // http://www.boost.org/LICENSE_1_0.txt)
377 
378 #endif
uint64_t deserializer_id() const
Definition: any.hpp:312
void load(iarchive &arc)
loads the any from a file.
Definition: any.hpp:233
any(const any &other)
Construct an any from another any.
Definition: any.hpp:150
holder(iarchive_soft_fail &arc)
Construct a holder from an archive.
Definition: any.hpp:299
The serialization input archive object which, provided with a reference to an istream, will read from the istream, providing deserialization capabilities.
Definition: iarchive.hpp:60
~any()
Destroy the contentss of this any.
Definition: any.hpp:154
const ValueType & as() const
Definition: any.hpp:171
bool empty() const
Returns true if the object does not contain any stored data.
Definition: any.hpp:157
const std::string type_name() const
Return the name of the internal type as a string.
Definition: any.hpp:228
any & operator=(const any &rhs)
Definition: any.hpp:206
std::ostream & print(std::ostream &out) const
Definition: any.hpp:320
static iholder * deserialize(iarchive_soft_fail &arc)
Definition: any.hpp:325
const std::type_info & type() const
Returns the type information of the stored data.
Definition: any.hpp:223
void deep_op_equal(const iholder *other)
Deep assignment.
Definition: any.hpp:305
When this archive is used to serialize an object, and the object does not support serialization...
Definition: oarchive.hpp:169
ValueType & as()
Definition: any.hpp:162
#define DASSERT_FALSE(cond)
Definition: assertions.hpp:365
any & swap(any &rhs)
Exchanges the contents of two any&#39;s.
Definition: any.hpp:185
iholder * clone() const
Clone a holder.
Definition: any.hpp:303
ValueType contents
The actual contents of the holder.
Definition: any.hpp:295
void save(oarchive &arc) const
Saves the any to a file. Caveats apply. See the main any docs.
Definition: any.hpp:242
holder(const ValueType &value)
Construct a holder from a value.
Definition: any.hpp:297
The serialization output archive object which, provided with a reference to an ostream, will write to the ostream, providing serialization capabilities.
Definition: oarchive.hpp:80
any()
default constructor. Creates an empty any
Definition: any.hpp:142
any & operator=(const ValueType &rhs)
Definition: any.hpp:195
static registry_type registry
Definition: any.hpp:351
bool is() const
Returns true if the contained type is of type ValueType.
Definition: any.hpp:179
const std::type_info & type() const
Get the type info of the holder.
Definition: any.hpp:301
static registry_map_type & get_global_registry()
#define DASSERT_TRUE(cond)
Definition: assertions.hpp:364
any(const ValueType &value)
Creates an any which stores the value.
Definition: any.hpp:146
When this archive is used to deserialize an object, and the object does not support serialization...
Definition: iarchive.hpp:147