1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 **************************************************************************/
19 #ifndef REALM_MIXED_HPP
20 #define REALM_MIXED_HPP
22 #include <cstdint> // int64_t - not part of C++03, not even required by C++11 (see C++11 section 18.4.1)
24 #include <cstddef> // size_t
27 #include <realm/binary_data.hpp>
28 #include <realm/data_type.hpp>
29 #include <realm/olddatetime.hpp>
30 #include <realm/string_data.hpp>
31 #include <realm/timestamp.hpp>
32 #include <realm/util/assert.hpp>
33 #include <realm/utilities.hpp>
38 /// This class represents a polymorphic Realm value.
40 /// At any particular moment an instance of this class stores a
41 /// definite value of a definite type. If, for instance, that is an
42 /// integer value, you may call get_int() to extract that value. You
43 /// may call get_type() to discover what type of value is currently
44 /// stored. Calling get_int() on an instance that does not store an
45 /// integer, has undefined behavior, and likewise for all the other
46 /// types that can be stored.
48 /// It is crucial to understand that the act of extracting a value of
49 /// a particular type requires definite knowledge about the stored
50 /// type. Calling a getter method for any particular type, that is not
51 /// the same type as the stored value, has undefined behavior.
53 /// While values of numeric types are contained directly in a Mixed
54 /// instance, character and binary data are merely referenced. A Mixed
55 /// instance never owns the referenced data, nor does it in any other
56 /// way attempt to manage its lifetime.
58 /// For compatibility with C style strings, when a string (character
59 /// data) is stored in a Realm database, it is always followed by a
60 /// terminating null character. This is also true when strings are
61 /// stored in a mixed type column. This means that in the following
62 /// code, if the 'mixed' value of the 8th row stores a string, then \c
63 /// c_str will always point to a null-terminated string:
67 /// const char* c_str = my_table[7].mixed.data(); // Always null-terminated
71 /// Note that this assumption does not hold in general for strings in
72 /// instances of Mixed. Indeed there is nothing stopping you from
73 /// constructing a new Mixed instance that refers to a string without
74 /// a terminating null character.
76 /// At the present time no soultion has been found that would allow
77 /// for a Mixed instance to directly store a reference to a table. The
78 /// problem is roughly as follows: From most points of view, the
79 /// desirable thing to do, would be to store the table reference in a
80 /// Mixed instance as a plain pointer without any ownership
81 /// semantics. This would have no negative impact on the performance
82 /// of copying and destroying Mixed instances, and it would serve just
83 /// fine for passing a table as argument when setting the value of an
84 /// entry in a mixed column. In that case a copy of the referenced
85 /// table would be inserted into the mixed column.
87 /// On the other hand, when retrieving a table reference from a mixed
88 /// column, storing it as a plain pointer in a Mixed instance is no
89 /// longer an acceptable option. The complex rules for managing the
90 /// lifetime of a Table instance, that represents a subtable,
91 /// necessitates the use of a "smart pointer" such as
92 /// TableRef. Enhancing the Mixed class to be able to act as a
93 /// TableRef would be possible, but would also lead to several new
94 /// problems. One problem is the risk of a Mixed instance outliving a
95 /// stack allocated Table instance that it references. This would be a
96 /// fatal error. Another problem is the impact that the nontrivial
97 /// table reference has on the performance of copying and destroying
105 Mixed(bool) noexcept;
106 Mixed(int64_t) noexcept;
107 Mixed(float) noexcept;
108 Mixed(double) noexcept;
109 Mixed(StringData) noexcept;
110 Mixed(BinaryData) noexcept;
111 Mixed(OldDateTime) noexcept;
112 Mixed(Timestamp) noexcept;
114 // These are shortcuts for Mixed(StringData(c_str)), and are
115 // needed to avoid unwanted implicit conversion of char* to bool.
116 Mixed(char* c_str) noexcept
120 Mixed(const char* c_str) noexcept
125 struct subtable_tag {
127 Mixed(subtable_tag) noexcept
136 DataType get_type() const noexcept
141 int64_t get_int() const noexcept;
142 bool get_bool() const noexcept;
143 float get_float() const noexcept;
144 double get_double() const noexcept;
145 StringData get_string() const noexcept;
146 BinaryData get_binary() const noexcept;
147 OldDateTime get_olddatetime() const noexcept;
148 Timestamp get_timestamp() const noexcept;
150 void set_int(int64_t) noexcept;
151 void set_bool(bool) noexcept;
152 void set_float(float) noexcept;
153 void set_double(double) noexcept;
154 void set_string(StringData) noexcept;
155 void set_binary(BinaryData) noexcept;
156 void set_binary(const char* data, size_t size) noexcept;
157 void set_olddatetime(OldDateTime) noexcept;
158 void set_timestamp(Timestamp) noexcept;
160 template <class Ch, class Tr>
161 friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>&, const Mixed&);
172 Timestamp m_timestamp;
177 // Note: We cannot compare two mixed values, since when the type of
178 // both is type_Table, we would have to compare the two tables, but
179 // the mixed values do not provide access to those tables.
181 // Note: The mixed values are specified as Wrap<Mixed>. If they were
182 // not, these operators would apply to simple comparisons, such as int
183 // vs int64_t, and cause ambiguity. This is because the constructors
184 // of Mixed are not explicit.
186 // Compare mixed with integer
188 bool operator==(Wrap<Mixed>, const T&) noexcept;
190 bool operator!=(Wrap<Mixed>, const T&) noexcept;
192 bool operator==(const T&, Wrap<Mixed>) noexcept;
194 bool operator!=(const T&, Wrap<Mixed>) noexcept;
196 // Compare mixed with boolean
197 bool operator==(Wrap<Mixed>, bool) noexcept;
198 bool operator!=(Wrap<Mixed>, bool) noexcept;
199 bool operator==(bool, Wrap<Mixed>) noexcept;
200 bool operator!=(bool, Wrap<Mixed>) noexcept;
202 // Compare mixed with float
203 bool operator==(Wrap<Mixed>, float);
204 bool operator!=(Wrap<Mixed>, float);
205 bool operator==(float, Wrap<Mixed>);
206 bool operator!=(float, Wrap<Mixed>);
208 // Compare mixed with double
209 bool operator==(Wrap<Mixed>, double);
210 bool operator!=(Wrap<Mixed>, double);
211 bool operator==(double, Wrap<Mixed>);
212 bool operator!=(double, Wrap<Mixed>);
214 // Compare mixed with string
215 bool operator==(Wrap<Mixed>, StringData) noexcept;
216 bool operator!=(Wrap<Mixed>, StringData) noexcept;
217 bool operator==(StringData, Wrap<Mixed>) noexcept;
218 bool operator!=(StringData, Wrap<Mixed>) noexcept;
219 bool operator==(Wrap<Mixed>, const char* c_str) noexcept;
220 bool operator!=(Wrap<Mixed>, const char* c_str) noexcept;
221 bool operator==(const char* c_str, Wrap<Mixed>) noexcept;
222 bool operator!=(const char* c_str, Wrap<Mixed>) noexcept;
223 bool operator==(Wrap<Mixed>, char* c_str) noexcept;
224 bool operator!=(Wrap<Mixed>, char* c_str) noexcept;
225 bool operator==(char* c_str, Wrap<Mixed>) noexcept;
226 bool operator!=(char* c_str, Wrap<Mixed>) noexcept;
228 // Compare mixed with binary data
229 bool operator==(Wrap<Mixed>, BinaryData) noexcept;
230 bool operator!=(Wrap<Mixed>, BinaryData) noexcept;
231 bool operator==(BinaryData, Wrap<Mixed>) noexcept;
232 bool operator!=(BinaryData, Wrap<Mixed>) noexcept;
234 // Compare mixed with date
235 bool operator==(Wrap<Mixed>, OldDateTime) noexcept;
236 bool operator!=(Wrap<Mixed>, OldDateTime) noexcept;
237 bool operator==(OldDateTime, Wrap<Mixed>) noexcept;
238 bool operator!=(OldDateTime, Wrap<Mixed>) noexcept;
240 // Compare mixed with Timestamp
241 bool operator==(Wrap<Mixed>, Timestamp) noexcept;
242 bool operator!=(Wrap<Mixed>, Timestamp) noexcept;
243 bool operator==(Timestamp, Wrap<Mixed>) noexcept;
244 bool operator!=(Timestamp, Wrap<Mixed>) noexcept;
248 inline Mixed::Mixed() noexcept
254 inline Mixed::Mixed(int64_t v) noexcept
260 inline Mixed::Mixed(bool v) noexcept
266 inline Mixed::Mixed(float v) noexcept
272 inline Mixed::Mixed(double v) noexcept
274 m_type = type_Double;
278 inline Mixed::Mixed(StringData v) noexcept
280 m_type = type_String;
285 inline Mixed::Mixed(BinaryData v) noexcept
287 m_type = type_Binary;
292 inline Mixed::Mixed(OldDateTime v) noexcept
294 m_type = type_OldDateTime;
295 m_date = v.get_olddatetime();
298 inline Mixed::Mixed(Timestamp v) noexcept
300 m_type = type_Timestamp;
304 inline int64_t Mixed::get_int() const noexcept
306 REALM_ASSERT(m_type == type_Int);
310 inline bool Mixed::get_bool() const noexcept
312 REALM_ASSERT(m_type == type_Bool);
316 inline float Mixed::get_float() const noexcept
318 REALM_ASSERT(m_type == type_Float);
322 inline double Mixed::get_double() const noexcept
324 REALM_ASSERT(m_type == type_Double);
328 inline StringData Mixed::get_string() const noexcept
330 REALM_ASSERT(m_type == type_String);
331 return StringData(m_data, m_size);
334 inline BinaryData Mixed::get_binary() const noexcept
336 REALM_ASSERT(m_type == type_Binary);
337 return BinaryData(m_data, m_size);
340 inline OldDateTime Mixed::get_olddatetime() const noexcept
342 REALM_ASSERT(m_type == type_OldDateTime);
346 inline Timestamp Mixed::get_timestamp() const noexcept
348 REALM_ASSERT(m_type == type_Timestamp);
352 inline void Mixed::set_int(int64_t v) noexcept
358 inline void Mixed::set_bool(bool v) noexcept
364 inline void Mixed::set_float(float v) noexcept
370 inline void Mixed::set_double(double v) noexcept
372 m_type = type_Double;
376 inline void Mixed::set_string(StringData v) noexcept
378 m_type = type_String;
383 inline void Mixed::set_binary(BinaryData v) noexcept
385 set_binary(v.data(), v.size());
388 inline void Mixed::set_binary(const char* data, size_t size) noexcept
390 m_type = type_Binary;
395 inline void Mixed::set_olddatetime(OldDateTime v) noexcept
397 m_type = type_OldDateTime;
398 m_date = v.get_olddatetime();
401 inline void Mixed::set_timestamp(Timestamp v) noexcept
403 m_type = type_Timestamp;
408 template <class Ch, class Tr>
409 inline std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& out, const Mixed& m)
426 out << StringData(m.m_data, m.m_size);
429 out << BinaryData(m.m_data, m.m_size);
431 case type_OldDateTime:
432 out << OldDateTime(m.m_date);
435 out << Timestamp(m.m_timestamp);
451 // Compare mixed with integer
454 inline bool operator==(Wrap<Mixed> a, const T& b) noexcept
456 return Mixed(a).get_type() == type_Int && Mixed(a).get_int() == b;
460 inline bool operator!=(Wrap<Mixed> a, const T& b) noexcept
462 return Mixed(a).get_type() != type_Int || Mixed(a).get_int() != b;
466 inline bool operator==(const T& a, Wrap<Mixed> b) noexcept
468 return type_Int == Mixed(b).get_type() && a == Mixed(b).get_int();
472 inline bool operator!=(const T& a, Wrap<Mixed> b) noexcept
474 return type_Int != Mixed(b).get_type() || a != Mixed(b).get_int();
478 // Compare mixed with boolean
480 inline bool operator==(Wrap<Mixed> a, bool b) noexcept
482 return Mixed(a).get_type() == type_Bool && Mixed(a).get_bool() == b;
485 inline bool operator!=(Wrap<Mixed> a, bool b) noexcept
487 return Mixed(a).get_type() != type_Bool || Mixed(a).get_bool() != b;
490 inline bool operator==(bool a, Wrap<Mixed> b) noexcept
492 return type_Bool == Mixed(b).get_type() && a == Mixed(b).get_bool();
495 inline bool operator!=(bool a, Wrap<Mixed> b) noexcept
497 return type_Bool != Mixed(b).get_type() || a != Mixed(b).get_bool();
501 // Compare mixed with float
503 inline bool operator==(Wrap<Mixed> a, float b)
505 return Mixed(a).get_type() == type_Float && Mixed(a).get_float() == b;
508 inline bool operator!=(Wrap<Mixed> a, float b)
510 return Mixed(a).get_type() != type_Float || Mixed(a).get_float() != b;
513 inline bool operator==(float a, Wrap<Mixed> b)
515 return type_Float == Mixed(b).get_type() && a == Mixed(b).get_float();
518 inline bool operator!=(float a, Wrap<Mixed> b)
520 return type_Float != Mixed(b).get_type() || a != Mixed(b).get_float();
524 // Compare mixed with double
526 inline bool operator==(Wrap<Mixed> a, double b)
528 return Mixed(a).get_type() == type_Double && Mixed(a).get_double() == b;
531 inline bool operator!=(Wrap<Mixed> a, double b)
533 return Mixed(a).get_type() != type_Double || Mixed(a).get_double() != b;
536 inline bool operator==(double a, Wrap<Mixed> b)
538 return type_Double == Mixed(b).get_type() && a == Mixed(b).get_double();
541 inline bool operator!=(double a, Wrap<Mixed> b)
543 return type_Double != Mixed(b).get_type() || a != Mixed(b).get_double();
547 // Compare mixed with string
549 inline bool operator==(Wrap<Mixed> a, StringData b) noexcept
551 return Mixed(a).get_type() == type_String && Mixed(a).get_string() == b;
554 inline bool operator!=(Wrap<Mixed> a, StringData b) noexcept
556 return Mixed(a).get_type() != type_String || Mixed(a).get_string() != b;
559 inline bool operator==(StringData a, Wrap<Mixed> b) noexcept
561 return type_String == Mixed(b).get_type() && a == Mixed(b).get_string();
564 inline bool operator!=(StringData a, Wrap<Mixed> b) noexcept
566 return type_String != Mixed(b).get_type() || a != Mixed(b).get_string();
569 inline bool operator==(Wrap<Mixed> a, const char* b) noexcept
571 return a == StringData(b);
574 inline bool operator!=(Wrap<Mixed> a, const char* b) noexcept
576 return a != StringData(b);
579 inline bool operator==(const char* a, Wrap<Mixed> b) noexcept
581 return StringData(a) == b;
584 inline bool operator!=(const char* a, Wrap<Mixed> b) noexcept
586 return StringData(a) != b;
589 inline bool operator==(Wrap<Mixed> a, char* b) noexcept
591 return a == StringData(b);
594 inline bool operator!=(Wrap<Mixed> a, char* b) noexcept
596 return a != StringData(b);
599 inline bool operator==(char* a, Wrap<Mixed> b) noexcept
601 return StringData(a) == b;
604 inline bool operator!=(char* a, Wrap<Mixed> b) noexcept
606 return StringData(a) != b;
610 // Compare mixed with binary data
612 inline bool operator==(Wrap<Mixed> a, BinaryData b) noexcept
614 return Mixed(a).get_type() == type_Binary && Mixed(a).get_binary() == b;
617 inline bool operator!=(Wrap<Mixed> a, BinaryData b) noexcept
619 return Mixed(a).get_type() != type_Binary || Mixed(a).get_binary() != b;
622 inline bool operator==(BinaryData a, Wrap<Mixed> b) noexcept
624 return type_Binary == Mixed(b).get_type() && a == Mixed(b).get_binary();
627 inline bool operator!=(BinaryData a, Wrap<Mixed> b) noexcept
629 return type_Binary != Mixed(b).get_type() || a != Mixed(b).get_binary();
633 // Compare mixed with date
635 inline bool operator==(Wrap<Mixed> a, OldDateTime b) noexcept
637 return Mixed(a).get_type() == type_OldDateTime && OldDateTime(Mixed(a).get_olddatetime()) == b;
640 inline bool operator!=(Wrap<Mixed> a, OldDateTime b) noexcept
642 return Mixed(a).get_type() != type_OldDateTime || OldDateTime(Mixed(a).get_olddatetime()) != b;
645 inline bool operator==(OldDateTime a, Wrap<Mixed> b) noexcept
647 return type_OldDateTime == Mixed(b).get_type() && a == OldDateTime(Mixed(b).get_olddatetime());
650 inline bool operator!=(OldDateTime a, Wrap<Mixed> b) noexcept
652 return type_OldDateTime != Mixed(b).get_type() || a != OldDateTime(Mixed(b).get_olddatetime());
655 // Compare mixed with Timestamp
657 inline bool operator==(Wrap<Mixed> a, Timestamp b) noexcept
659 return Mixed(a).get_type() == type_Timestamp && Timestamp(Mixed(a).get_timestamp()) == b;
662 inline bool operator!=(Wrap<Mixed> a, Timestamp b) noexcept
664 return Mixed(a).get_type() != type_Timestamp || Timestamp(Mixed(a).get_timestamp()) != b;
667 inline bool operator==(Timestamp a, Wrap<Mixed> b) noexcept
669 return type_Timestamp == Mixed(b).get_type() && a == Timestamp(Mixed(b).get_timestamp());
672 inline bool operator!=(Timestamp a, Wrap<Mixed> b) noexcept
674 return type_Timestamp != Mixed(b).get_type() || a != Timestamp(Mixed(b).get_timestamp());
680 #endif // REALM_MIXED_HPP