RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
Dict.h
Go to the documentation of this file.
1//
2// Copyright (C) 2003-2026 Greg Landrum and other RDKit contributors
3//
4// @@ All Rights Reserved @@
5// This file is part of the RDKit.
6// The contents are covered by the terms of the BSD license
7// which is included in the file license.txt, found at the root
8// of the RDKit source tree.
9//
10/*! \file Dict.h
11
12 \brief Defines the Dict class
13
14*/
15#include <RDGeneral/export.h>
16#ifndef RD_DICT_H_012020
17#define RD_DICT_H_012020
18
19#include <string>
20#include <string_view>
21#include <vector>
22#include "RDValue.h"
23#include "Exceptions.h"
25#include <boost/lexical_cast.hpp>
27
28namespace RDKit {
29typedef std::vector<std::string> STR_VECT;
30
31//! \brief The \c Dict class can be used to store objects of arbitrary
32//! type keyed by \c strings.
33//!
34//! The actual storage is done using \c RDValue objects.
35//!
37 public:
38 struct Pair {
39 std::string key;
41
42 Pair() : key(), val() {}
43 explicit Pair(std::string s) : key(std::move(s)), val() {}
44 explicit Pair(std::string_view s) : key(std::string(s)), val() {}
45 Pair(std::string s, const RDValue &v) : key(std::move(s)), val(v) {}
46 Pair(std::string_view s, const RDValue &v) : key(std::string(s)), val(v) {}
47 // In the case you are holding onto an rdvalue outside of a dictionary
48 // or other container, you kust call cleanup to release non POD memory.
50 };
51
52 typedef std::vector<Pair> DataType;
53
54 Dict() {}
55
56 Dict(const Dict &other) : _data(other._data) {
57 _hasNonPodData = other._hasNonPodData;
58 if (other._hasNonPodData) { // other has non pod data, need to copy
59 std::vector<Pair> data(other._data.size());
60 _data.swap(data);
61 for (size_t i = 0; i < _data.size(); ++i) {
62 _data[i].key = other._data[i].key;
63 copy_rdvalue(_data[i].val, other._data[i].val);
64 }
65 }
66 }
67
68 Dict(Dict &&other) noexcept = default;
69
71 reset(); // to clear pointers if necessary
72 }
73
74 void update(const Dict &other, bool preserveExisting = false) {
75 if (!preserveExisting) {
76 *this = other;
77 } else {
78 if (other._hasNonPodData) {
79 _hasNonPodData = true;
80 }
81 for (const auto &opair : other._data) {
82 Pair *target = nullptr;
83 for (auto &dpair : _data) {
84 if (dpair.key == opair.key) {
85 target = &dpair;
86 break;
87 }
88 }
89
90 if (!target) {
91 // need to create blank entry and copy
92 _data.push_back(Pair(opair.key));
93 copy_rdvalue(_data.back().val, opair.val);
94 } else {
95 // just copy
96 copy_rdvalue(target->val, opair.val);
97 }
98 }
99 }
100 }
101
102 Dict &operator=(const Dict &other) {
103 if (this == &other) {
104 return *this;
105 }
106 if (_hasNonPodData) {
107 reset();
108 }
109
110 if (other._hasNonPodData) {
111 std::vector<Pair> data(other._data.size());
112 _data.swap(data);
113 for (size_t i = 0; i < _data.size(); ++i) {
114 _data[i].key = other._data[i].key;
115 copy_rdvalue(_data[i].val, other._data[i].val);
116 }
117 } else {
118 _data = other._data;
119 }
120 _hasNonPodData = other._hasNonPodData;
121 return *this;
122 }
123
124 Dict &operator=(Dict &&other) noexcept {
125 if (this == &other) {
126 return *this;
127 }
128 if (_hasNonPodData) {
129 reset();
130 }
131 _hasNonPodData = other._hasNonPodData;
132 other._hasNonPodData = false;
133 _data = std::move(other._data);
134 return *this;
135 }
136
137 //----------------------------------------------------------
138 //! \brief Returns the number of entries in the dictionary
139 std::size_t size() const { return _data.size(); }
140
141 //! \brief Returns whether the dictionary is empty
142 bool empty() const { return _data.empty(); }
143
144 using const_iterator = DataType::const_iterator;
145 const_iterator begin() const { return _data.begin(); }
146 const_iterator end() const { return _data.end(); }
147
148 //! \brief Appends a populated Pair to the dictionary.
149 void insert(Pair &&pair) {
150 _hasNonPodData |= pair.val.needsCleanup();
151 _data.push_back(std::move(pair));
152 }
153
154 //! \brief Bulk-appends a vector of Pairs, moving them into the dictionary.
155 void extend(std::vector<Pair> &&pairs) {
156 for (auto &p : pairs) {
157 _hasNonPodData |= p.val.needsCleanup();
158 }
159 _data.insert(_data.end(), std::make_move_iterator(pairs.begin()),
160 std::make_move_iterator(pairs.end()));
161 }
162
163 //! \brief Returns a const reference to the RDValue for a key.
164 //! Throws KeyErrorException if the key is not found.
165 const RDValue &getRDValue(const std::string_view what) const {
166 for (const auto &data : _data) {
167 if (data.key == what) {
168 return data.val;
169 }
170 }
171 throw KeyErrorException(what);
172 }
173
174 //----------------------------------------------------------
175
176 //! \brief Returns whether or not the dictionary contains a particular
177 //! key.
178 bool hasVal(const std::string_view what) const {
179 for (const auto &data : _data) {
180 if (data.key == what) {
181 return true;
182 }
183 }
184 return false;
185 }
186
187 //----------------------------------------------------------
188 //! Returns the set of keys in the dictionary
189 /*!
190 \return a \c STR_VECT
191 */
192 STR_VECT keys() const {
193 STR_VECT res;
194 res.reserve(_data.size());
195 for (const auto &item : _data) {
196 res.push_back(item.key);
197 }
198 return res;
199 }
200
201 //----------------------------------------------------------
202 //! \brief Gets the value associated with a particular key
203 /*!
204 \param what the key to lookup
205 \param res a reference used to return the result
206
207 <b>Notes:</b>
208 - If \c res is a \c std::string, every effort will be made
209 to convert the specified element to a string using the
210 \c boost::lexical_cast machinery.
211 - If the dictionary does not contain the key \c what,
212 a KeyErrorException will be thrown.
213 */
214 template <typename T>
215 void getVal(const std::string_view what, T &res) const {
216 res = getVal<T>(what);
217 }
218
219 //! \overload
220 template <typename T>
221 T getVal(const std::string_view what) const {
222 for (auto &data : _data) {
223 if (data.key == what) {
224 return from_rdvalue<T>(data.val);
225 }
226 }
227 throw KeyErrorException(what);
228 }
229
230 //! \overload
231 void getVal(const std::string_view what, std::string &res) const {
232 for (const auto &i : _data) {
233 if (i.key == what) {
234 rdvalue_tostring(i.val, res);
235 return;
236 }
237 }
238 throw KeyErrorException(what);
239 }
240
241 //----------------------------------------------------------
242 //! \brief Potentially gets the value associated with a particular key
243 //! returns true on success/false on failure.
244 /*!
245 \param what the key to lookup
246 \param res a reference used to return the result
247
248 <b>Notes:</b>
249 - If \c res is a \c std::string, every effort will be made
250 to convert the specified element to a string using the
251 \c boost::lexical_cast machinery.
252 - If the dictionary does not contain the key \c what,
253 a KeyErrorException will be thrown.
254 */
255 template <typename T>
256 bool getValIfPresent(const std::string_view what, T &res) const {
257 for (const auto &data : _data) {
258 if (data.key == what) {
259 res = from_rdvalue<T>(data.val);
260 return true;
261 }
262 }
263 return false;
264 }
265
266 //! \overload
267 bool getValIfPresent(const std::string_view what, std::string &res) const {
268 for (const auto &i : _data) {
269 if (i.key == what) {
270 rdvalue_tostring(i.val, res);
271 return true;
272 }
273 }
274 return false;
275 }
276
277 //----------------------------------------------------------
278 //! \brief Sets the value associated with a key
279 /*!
280
281 \param what the key to set
282 \param val the value to store
283
284 <b>Notes:</b>
285 - If \c val is a <tt>const char *</tt>, it will be converted
286 to a \c std::string for storage.
287 - If the dictionary already contains the key \c what,
288 the value will be replaced.
289 */
290 template <typename T>
291 void setVal(const std::string_view what, T &val) {
292 static_assert(!std::is_same_v<T, std::string_view>,
293 "T cannot be string_view");
294 if (what.empty()) {
295 throw ValueErrorException("Cannot set value with empty key");
296 }
297 _hasNonPodData = true;
298 for (auto &&data : _data) {
299 if (data.key == what) {
300 RDValue::cleanup_rdvalue(data.val);
301 data.val = val;
302 return;
303 }
304 }
305 _data.push_back(Pair(what, val));
306 }
307
308 template <typename T>
309 void setPODVal(const std::string_view what, T val) {
310 static_assert(!std::is_same_v<T, std::string_view>,
311 "T cannot be string_view");
312 if (what.empty()) {
313 throw ValueErrorException("Cannot set value with empty key");
314 }
315 // don't change the hasNonPodData status
316 for (auto &&data : _data) {
317 if (data.key == what) {
318 RDValue::cleanup_rdvalue(data.val);
319 data.val = val;
320 return;
321 }
322 }
323 _data.push_back(Pair(what, val));
324 }
325
326 void setVal(const std::string_view what, bool val) {
327 if (what.empty()) {
328 throw ValueErrorException("Cannot set value with empty key");
329 }
330 setPODVal(what, val);
331 }
332
333 void setVal(const std::string_view what, double val) {
334 if (what.empty()) {
335 throw ValueErrorException("Cannot set value with empty key");
336 }
337 setPODVal(what, val);
338 }
339
340 void setVal(const std::string_view what, float val) {
341 if (what.empty()) {
342 throw ValueErrorException("Cannot set value with empty key");
343 }
344 setPODVal(what, val);
345 }
346 void setVal(const std::string_view what, int val) {
347 if (what.empty()) {
348 throw ValueErrorException("Cannot set value with empty key");
349 }
350 setPODVal(what, val);
351 }
352
353 void setVal(const std::string_view what, unsigned int val) {
354 if (what.empty()) {
355 throw ValueErrorException("Cannot set value with empty key");
356 }
357 setPODVal(what, val);
358 }
359
360 //! \overload
361 void setVal(const std::string_view what, const char *val) {
362 if (what.empty()) {
363 throw ValueErrorException("Cannot set value with empty key");
364 }
365 std::string h(val);
366 setVal(what, h);
367 }
368
369 //----------------------------------------------------------
370 //! \brief Clears the value associated with a particular key,
371 //! removing the key from the dictionary.
372 /*!
373
374 \param what the key to clear
375
376 */
377 void clearVal(const std::string_view what) {
378 for (auto it = _data.begin(); it < _data.end(); ++it) {
379 if (it->key == what) {
380 if (_hasNonPodData) {
382 }
383 _data.erase(it);
384 return;
385 }
386 }
387 }
388
389 //----------------------------------------------------------
390 //! \brief Clears all keys (and values) from the dictionary.
391 //!
392 void reset() {
393 if (_hasNonPodData) {
394 for (auto &&data : _data) {
395 RDValue::cleanup_rdvalue(data.val);
396 }
397 }
398 DataType data;
399 _data.swap(data);
400 }
401
402 private:
403 DataType _data{}; //!< the actual dictionary
404 bool _hasNonPodData{false}; // if true, need a deep copy
405 // (copy_rdvalue)
406};
407
408template <>
409inline std::string Dict::getVal<std::string>(
410 const std::string_view what) const {
411 std::string res;
412 getVal(what, res);
413 return res;
414}
415
416// Utility class for holding a Dict::Pair
417// Dict::Pairs require containers for memory management
418// This utility class covers cleanup and copying
419class PairHolder : public Dict::Pair {
420 public:
422
423 explicit PairHolder(const PairHolder &p) : Pair(p.key) {
424 copy_rdvalue(this->val, p.val);
425 }
426
427 explicit PairHolder(PairHolder &&p) : Pair(p.key) {
428 this->val = p.val;
429 p.val.type = RDTypeTag::EmptyTag;
430 }
431
432 explicit PairHolder(Dict::Pair &&p) : Pair(p.key) {
433 this->val = p.val;
434 p.val.type = RDTypeTag::EmptyTag;
435 }
436
438};
439} // namespace RDKit
440#endif
Class to allow us to throw a KeyError from C++ and have it make it back to Python.
Definition Exceptions.h:57
void setVal(const std::string_view what, float val)
Definition Dict.h:340
void setVal(const std::string_view what, int val)
Definition Dict.h:346
DataType::const_iterator const_iterator
Definition Dict.h:144
Dict(Dict &&other) noexcept=default
T getVal(const std::string_view what) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition Dict.h:221
void extend(std::vector< Pair > &&pairs)
Bulk-appends a vector of Pairs, moving them into the dictionary.
Definition Dict.h:155
void setPODVal(const std::string_view what, T val)
Definition Dict.h:309
void reset()
Clears all keys (and values) from the dictionary.
Definition Dict.h:392
Dict & operator=(const Dict &other)
Definition Dict.h:102
STR_VECT keys() const
Returns the set of keys in the dictionary.
Definition Dict.h:192
void insert(Pair &&pair)
Appends a populated Pair to the dictionary.
Definition Dict.h:149
const_iterator end() const
Definition Dict.h:146
void setVal(const std::string_view what, T &val)
Sets the value associated with a key.
Definition Dict.h:291
bool hasVal(const std::string_view what) const
Returns whether or not the dictionary contains a particular key.
Definition Dict.h:178
bool getValIfPresent(const std::string_view what, std::string &res) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition Dict.h:267
const_iterator begin() const
Definition Dict.h:145
void clearVal(const std::string_view what)
Clears the value associated with a particular key, removing the key from the dictionary.
Definition Dict.h:377
void setVal(const std::string_view what, const char *val)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition Dict.h:361
const RDValue & getRDValue(const std::string_view what) const
Returns a const reference to the RDValue for a key. Throws KeyErrorException if the key is not found.
Definition Dict.h:165
~Dict()
Definition Dict.h:70
void setVal(const std::string_view what, bool val)
Definition Dict.h:326
void getVal(const std::string_view what, std::string &res) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition Dict.h:231
std::vector< Pair > DataType
Definition Dict.h:52
void setVal(const std::string_view what, double val)
Definition Dict.h:333
Dict & operator=(Dict &&other) noexcept
Definition Dict.h:124
void update(const Dict &other, bool preserveExisting=false)
Definition Dict.h:74
void setVal(const std::string_view what, unsigned int val)
Definition Dict.h:353
void getVal(const std::string_view what, T &res) const
Gets the value associated with a particular key.
Definition Dict.h:215
bool empty() const
Returns whether the dictionary is empty.
Definition Dict.h:142
Dict(const Dict &other)
Definition Dict.h:56
bool getValIfPresent(const std::string_view what, T &res) const
Potentially gets the value associated with a particular key returns true on success/false on failure.
Definition Dict.h:256
std::size_t size() const
Returns the number of entries in the dictionary.
Definition Dict.h:139
PairHolder(PairHolder &&p)
Definition Dict.h:427
PairHolder(Dict::Pair &&p)
Definition Dict.h:432
PairHolder(const PairHolder &p)
Definition Dict.h:423
Class to allow us to throw a ValueError from C++ and have it make it back to Python.
Definition Exceptions.h:41
#define RDKIT_RDGENERAL_EXPORT
Definition export.h:449
Std stuff.
std::vector< std::string > STR_VECT
Definition Dict.h:29
bool rdvalue_tostring(RDValue_cast_t val, std::string &res)
Definition RDValue.h:192
void copy_rdvalue(RDValue &dest, const RDValue &src)
boost::enable_if< boost::is_arithmetic< T >, T >::type from_rdvalue(RDValue_cast_t arg)
Definition RDValue.h:270
Pair(std::string s)
Definition Dict.h:43
std::string key
Definition Dict.h:39
void cleanup()
Definition Dict.h:49
Pair(std::string s, const RDValue &v)
Definition Dict.h:45
Pair(std::string_view s)
Definition Dict.h:44
RDValue val
Definition Dict.h:40
Pair(std::string_view s, const RDValue &v)
Definition Dict.h:46
static void cleanup_rdvalue(RDValue v)