RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
RDValue-taggedunion.h
Go to the documentation of this file.
1// Copyright (c) 2015, Novartis Institutes for BioMedical Research Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following
12// disclaimer in the documentation and/or other materials provided
13// with the distribution.
14// * Neither the name of Novartis Institutes for BioMedical Research Inc.
15// nor the names of its contributors may be used to endorse or promote
16// products derived from this software without specific prior written
17// permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30//
31#include <RDGeneral/export.h>
32#ifndef RDKIT_RDVALUE_TAGGED_UNION_H
33#define RDKIT_RDVALUE_TAGGED_UNION_H
34
35#include <cassert>
36#include "Invariant.h"
37#include <iomanip>
38#include <sstream>
39#include <vector>
40#include <cstdint>
42#include <any>
43#include <boost/utility.hpp>
44#include <boost/lexical_cast.hpp>
45#include <boost/numeric/conversion/cast.hpp>
46#include <boost/type_traits/is_floating_point.hpp>
48#include "LocaleSwitcher.h"
49
50#define RDVALUE_HASBOOL
51
52namespace RDKit {
53
54// RDValue does not dynamically create POD types (kind of like
55// cdiggins::any) However, it doesn't use RTTI type info
56// directly, it uses a companion short valued type
57// to determine what to do.
58// For unregistered types, it falls back to std::any.
59// The Size of an RDAny is (sizeof(double) + sizeof(short) == 10 bytes
60// (aligned to actually 16 so hard to pass as value type)
61//
62// For the sake of compatibility, errors throw std::bad_any_cast
63//
64// Examples:
65//
66// RDAny v(2.);
67// v = 1;
68// std::vector<double> d;
69// v == d;
70// v.asDoubleVect().push_back(4.)
71// rdany_cast<std::vector<double>(v).push_back(4.)
72//
73// Falls back to std::any for non registered types
74// v = boost::shared_ptr<ROMol>(new ROMol(m));
75//
76
77// RDValue does not manange memory of non-pod data
78// this must be done externally (string, Any, vector...)
79// Tagged union
80
81namespace RDTypeTag {
82const short EmptyTag = 0;
83const short IntTag = 1;
84const short DoubleTag = 2;
85const short StringTag = 3;
86const short FloatTag = 4;
87const short BoolTag = 5;
88const short UnsignedIntTag = 6;
89const short AnyTag = 7;
90const short VecDoubleTag = 8;
91const short VecFloatTag = 9;
92const short VecIntTag = 10;
93const short VecUnsignedIntTag = 11;
94const short VecStringTag = 12;
95template <class T>
96inline short GetTag() {
97 return AnyTag;
98}
99template <>
100inline short GetTag<double>() {
101 return DoubleTag;
102}
103template <>
104inline short GetTag<float>() {
105 return FloatTag;
106}
107template <>
108inline short GetTag<int>() {
109 return IntTag;
110}
111template <>
112inline short GetTag<unsigned int>() {
113 return UnsignedIntTag;
114}
115template <>
116inline short GetTag<bool>() {
117 return BoolTag;
118}
119template <>
120inline short GetTag<std::string>() {
121 return StringTag;
122}
123template <>
125 return VecDoubleTag;
126}
127template <>
129 return VecFloatTag;
130}
131template <>
132inline short GetTag<std::vector<int>>() {
133 return VecIntTag;
134}
135template <>
137 return VecUnsignedIntTag;
138}
139template <>
141 return VecStringTag;
142}
143template <>
144inline short GetTag<std::any>() {
145 return AnyTag;
146}
147
148namespace detail {
149union Value {
150 double d;
151 float f;
152 int i;
153 unsigned u;
154 bool b;
155 std::string *s;
156 std::any *a;
157 std::vector<double> *vd;
158 std::vector<float> *vf;
159 std::vector<int> *vi;
160 std::vector<unsigned int> *vu;
161 std::vector<std::string> *vs;
162
163 inline Value() {}
164 inline Value(double v) : d(v) {}
165 inline Value(float v) : f(v) {}
166 inline Value(int v) : i(v) {}
167 inline Value(unsigned int v) : u(v) {}
168 inline Value(bool v) : b(v) {}
169 inline Value(std::string *v) : s(v) {}
170 inline Value(std::any *v) : a(v) {}
171 inline Value(std::vector<double> *v) : vd(v) {}
172 inline Value(std::vector<float> *v) : vf(v) {}
173 inline Value(std::vector<int> *v) : vi(v) {}
174 inline Value(std::vector<unsigned int> *v) : vu(v) {}
175 inline Value(std::vector<std::string> *v) : vs(v) {}
176};
177
178template <class T>
179inline T *valuePtrCast(Value value) {
180 return std::any_cast<T *>(*value.a);
181}
182template <>
183inline std::any *valuePtrCast<std::any>(Value value) {
184 return value.a;
185}
186
187template <>
188inline std::string *valuePtrCast<std::string>(Value value) {
189 return value.s;
190}
191template <>
192inline std::vector<double> *valuePtrCast<std::vector<double>>(Value value) {
193 return value.vd;
194}
195template <>
196inline std::vector<float> *valuePtrCast<std::vector<float>>(Value value) {
197 return value.vf;
198}
199template <>
200inline std::vector<int> *valuePtrCast<std::vector<int>>(Value value) {
201 return value.vi;
202}
203template <>
204inline std::vector<unsigned int> *valuePtrCast<std::vector<unsigned int>>(
205 Value value) {
206 return value.vu;
207}
208template <>
209inline std::vector<std::string> *valuePtrCast<std::vector<std::string>>(
210 Value value) {
211 return value.vs;
212}
213} // namespace detail
214} // namespace RDTypeTag
215
216struct RDValue {
218 short type;
219 short reserved_tag = 0; // 16 bit alignment
220
221 inline RDValue() : value(0.0), type(RDTypeTag::EmptyTag) {}
222 // Pod Style (Direct storage)
223 inline RDValue(double v) : value(v), type(RDTypeTag::DoubleTag) {}
224 inline RDValue(float v) : value(v), type(RDTypeTag::FloatTag) {}
225 inline RDValue(int v) : value(v), type(RDTypeTag::IntTag) {}
226 inline RDValue(unsigned v) : value(v), type(RDTypeTag::UnsignedIntTag) {}
227 inline RDValue(bool v) : value(v), type(RDTypeTag::BoolTag) {}
228
229 inline RDValue(std::any *v) : value(v), type(RDTypeTag::AnyTag) {}
230
231 // Copies passed in pointers
232 inline RDValue(const std::any &v)
233 : value(new std::any(v)), type(RDTypeTag::AnyTag) {}
234 inline RDValue(const std::string &v)
235 : value(new std::string(v)), type(RDTypeTag::StringTag) {}
236 template <class T>
237 inline RDValue(const T &v)
238 : value(new std::any(v)), type(RDTypeTag::AnyTag) {}
239
240 inline RDValue(const std::vector<double> &v)
241 : value(new std::vector<double>(v)), type(RDTypeTag::VecDoubleTag) {}
242 inline RDValue(const std::vector<float> &v)
243 : value(new std::vector<float>(v)), type(RDTypeTag::VecFloatTag) {}
244 inline RDValue(const std::vector<int> &v)
245 : value(new std::vector<int>(v)), type(RDTypeTag::VecIntTag) {}
246 inline RDValue(const std::vector<unsigned int> &v)
247 : value(new std::vector<unsigned int>(v)),
248 type(RDTypeTag::VecUnsignedIntTag) {}
249 inline RDValue(const std::vector<std::string> &v)
250 : value(new std::vector<std::string>(v)), type(RDTypeTag::VecStringTag) {}
251
252 short getTag() const { return type; }
253
254 // ptrCast - unsafe, use rdvalue_cast instead.
255 template <class T>
256 inline T *ptrCast() const {
258 }
259
260 // RDValue doesn't have an explicit destructor, it must
261 // be wrapped in a container.
262 // The idea is that POD types don't need to be destroyed
263 // and this allows the container optimization possibilities.
264 bool needsCleanup() const {
265 switch (type) {
273 return true;
274 default:
275 return false;
276 }
277 }
278
279 // Keep in sync with needsCleanup() above.
280 void destroy() {
281 switch (type) {
283 delete value.s;
284 break;
286 delete value.a;
287 break;
289 delete value.vd;
290 break;
292 delete value.vf;
293 break;
295 delete value.vi;
296 break;
298 delete value.vu;
299 break;
301 delete value.vs;
302 break;
303 default:
304 break;
305 }
307 }
308
309 static // Given a type and an RDAnyValue - delete the appropriate structure
310 inline void
312 rdvalue.destroy();
313 }
314};
315
316/////////////////////////////////////////////////////////////////////////////////////
317// Given two RDValue::Values - copy the appropriate structure
318// RDValue doesn't have a copy constructor, the default
319// copy act's like a move for better value semantics.
320// Containers may need to copy though.
321inline void copy_rdvalue(RDValue &dest, const RDValue &src) {
322 if (&dest == &src) { // don't copy over yourself
323 return;
324 }
325 dest.destroy();
326 dest.type = src.type;
327 switch (src.type) {
329 dest.value.s = new std::string(*src.value.s);
330 break;
332 dest.value.a = new std::any(*src.value.a);
333 break;
335 dest.value.vd = new std::vector<double>(*src.value.vd);
336 break;
338 dest.value.vf = new std::vector<float>(*src.value.vf);
339 break;
341 dest.value.vi = new std::vector<int>(*src.value.vi);
342 break;
344 dest.value.vu = new std::vector<unsigned int>(*src.value.vu);
345 break;
347 dest.value.vs = new std::vector<std::string>(*src.value.vs);
348 break;
349 default:
350 dest = src;
351 }
352}
353
354#ifdef RDK_32BIT_BUILD
355// avoid register pressure and spilling on 32 bit systems
356typedef const RDValue &RDValue_cast_t;
357#else
358typedef RDValue RDValue_cast_t;
359#endif
360
361/////////////////////////////////////////////////////////////////////////////////////
362// rdvalue_is<T>
363
364template <class T>
365inline bool rdvalue_is(RDValue_cast_t v) {
366 const short tag =
368
369 // If we are an Any tag, check the any type info
370 // see the template specialization below if we are
371 // looking for a boost any directly
372 if (v.getTag() == RDTypeTag::AnyTag) {
373 return v.value.a->type() == typeid(T);
374 }
375
376 if (v.getTag() == tag) {
377 return true;
378 }
379
380 return false;
381}
382
383template <>
385 // If we are explicitly looking for a std::any
386 // then just check the top level tag
387 const short tag = RDTypeTag::GetTag<std::any>();
388 return v.getTag() == tag;
389}
390/////////////////////////////////////////////////////////////////////////////////////
391// rdvalue_cast<T>
392//
393// POD types do not support reference semantics. Other types do.
394// rdvalue_cast<const std::vector<double> &>(RDValue); // ok
395// rdvalue_cast<const float &>(RDValue); // bad_any_cast
396
397// Get stuff stored in boost any
398template <class T>
399inline T rdvalue_cast(RDValue_cast_t v) {
400 // Disable reference and pointer casts to POD data.
401 BOOST_STATIC_ASSERT(!(
402 (boost::is_pointer<T>::value &&
403 (boost::is_integral<typename boost::remove_pointer<T>::type>::value ||
404 boost::is_floating_point<
405 typename boost::remove_pointer<T>::type>::value)) ||
406 (boost::is_reference<T>::value &&
407 (boost::is_integral<typename boost::remove_reference<T>::type>::value ||
408 boost::is_floating_point<
409 typename boost::remove_reference<T>::type>::value))));
410
411 if (rdvalue_is<std::any>(v)) {
412 return std::any_cast<T>(*v.ptrCast<std::any>());
413 }
414 throw std::bad_any_cast();
415}
416
417// POD casts
418template <>
420 if (rdvalue_is<double>(v)) {
421 return v.value.d;
422 }
423 if (rdvalue_is<float>(v)) {
424 return v.value.f;
425 }
426 throw std::bad_any_cast();
427}
428
429template <>
431 if (rdvalue_is<float>(v)) {
432 return v.value.f;
433 }
434 if (rdvalue_is<double>(v)) {
435 return boost::numeric_cast<float>(v.value.d);
436 }
437 throw std::bad_any_cast();
438}
439
440template <>
442 if (rdvalue_is<int>(v)) {
443 return v.value.i;
444 }
446 return boost::numeric_cast<int>(v.value.u);
447 }
448 throw std::bad_any_cast();
449}
450
451template <>
453 if (rdvalue_is<int>(v)) {
454 return boost::numeric_cast<std::int8_t>(v.value.i);
455 }
457 return boost::numeric_cast<std::int8_t>(v.value.u);
458 }
459 throw std::bad_any_cast();
460}
461
462template <>
464 if (rdvalue_is<int>(v)) {
465 return boost::numeric_cast<std::int16_t>(v.value.i);
466 }
468 return boost::numeric_cast<std::int16_t>(v.value.u);
469 }
470 throw std::bad_any_cast();
471}
472
473template <>
475 if (rdvalue_is<int>(v)) {
476 return static_cast<std::int64_t>(v.value.i);
477 }
479 return static_cast<std::int64_t>(v.value.u);
480 }
481 if (rdvalue_is<std::any>(v)) {
482 return std::any_cast<std::int64_t>(*v.ptrCast<std::any>());
483 }
484 throw std::bad_any_cast();
485}
486
487template <>
490 return v.value.u;
491 }
492 if (rdvalue_is<int>(v)) {
493 return boost::numeric_cast<unsigned int>(v.value.i);
494 }
495 throw std::bad_any_cast();
496}
497
498template <>
500 if (rdvalue_is<int>(v)) {
501 return boost::numeric_cast<std::uint8_t>(v.value.i);
502 }
504 return boost::numeric_cast<std::uint8_t>(v.value.u);
505 }
506 throw std::bad_any_cast();
507}
508
509template <>
511 if (rdvalue_is<int>(v)) {
512 return boost::numeric_cast<std::uint16_t>(v.value.i);
513 }
515 return boost::numeric_cast<std::uint16_t>(v.value.u);
516 }
517 throw std::bad_any_cast();
518}
519
520template <>
523 return static_cast<std::uint64_t>(v.value.u);
524 }
525 if (rdvalue_is<int>(v)) {
526 return boost::numeric_cast<std::uint64_t>(v.value.i);
527 }
528 if (rdvalue_is<std::any>(v)) {
529 return std::any_cast<std::uint64_t>(*v.ptrCast<std::any>());
530 }
531 throw std::bad_any_cast();
532}
533
534template <>
536 if (rdvalue_is<bool>(v)) {
537 return v.value.b;
538 }
539 throw std::bad_any_cast();
540}
541
542} // namespace RDKit
543#endif
std::any * valuePtrCast< std::any >(Value value)
std::string * valuePtrCast< std::string >(Value value)
static const std::uint64_t UnsignedIntTag
static const std::uint64_t StringTag
std::uint64_t GetTag< std::any >()
std::uint64_t GetTag< bool >()
static const std::uint64_t VecStringTag
static const std::uint64_t VecIntTag
static const std::uint64_t FloatTag
std::uint64_t GetTag< std::string >()
static const std::uint64_t VecUnsignedIntTag
static const std::uint64_t DoubleTag
std::uint64_t GetTag()
static const std::uint64_t IntTag
std::uint64_t GetTag< int >()
std::uint64_t GetTag< float >()
std::uint64_t GetTag< unsigned int >()
static const std::uint64_t AnyTag
std::uint64_t GetTag< double >()
static const std::uint64_t VecFloatTag
static const std::uint64_t VecDoubleTag
static const std::uint64_t BoolTag
Std stuff.
std::int16_t rdvalue_cast< std::int16_t >(RDValue_cast_t v)
int rdvalue_cast< int >(RDValue_cast_t v)
unsigned int rdvalue_cast< unsigned int >(RDValue_cast_t v)
std::uint8_t rdvalue_cast< std::uint8_t >(RDValue_cast_t v)
bool rdvalue_is(const RDValue_cast_t)
std::int64_t rdvalue_cast< std::int64_t >(RDValue_cast_t v)
std::uint64_t rdvalue_cast< std::uint64_t >(RDValue_cast_t v)
double rdvalue_cast< double >(RDValue_cast_t v)
void copy_rdvalue(RDValue &dest, const RDValue &src)
T rdvalue_cast(RDValue_cast_t v)
std::int8_t rdvalue_cast< std::int8_t >(RDValue_cast_t v)
std::uint16_t rdvalue_cast< std::uint16_t >(RDValue_cast_t v)
RDValue RDValue_cast_t
bool rdvalue_cast< bool >(RDValue_cast_t v)
float rdvalue_cast< float >(RDValue_cast_t v)
bool rdvalue_is< std::any >(RDValue_cast_t v)
RDValue(const std::vector< int > &v)
RDValue(const std::vector< std::string > &v)
RDValue(const std::vector< double > &v)
RDValue(const std::vector< unsigned int > &v)
std::uint64_t getTag() const
bool needsCleanup() const
RDTypeTag::detail::Value value
RDValue(const std::string &v)
RDValue(const std::vector< float > &v)
RDValue(const std::any &v)
static void cleanup_rdvalue(RDValue &rdvalue)
Value(std::vector< std::string > *v)
std::vector< std::string > * vs
Value(std::vector< unsigned int > *v)
Value(std::vector< double > *v)
std::vector< unsigned int > * vu