palimpsest v3.0.0
Loading...
Searching...
No Matches
Dictionary.h
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2022 Stéphane Caron
3// Copyright 2024 Inria
4/*
5 * This file incorporates work covered by the following copyright and
6 * permission notice:
7 *
8 * Configuration and DataStore classes of mc_rtc
9 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
10 * SPDX-License-Identifier: BSD-2-Clause
11 */
12
13#pragma once
14
15#include <spdlog/spdlog.h>
16
17#include <Eigen/Core>
18#include <functional>
19#include <memory>
20#include <stdexcept>
21#include <string>
22#include <unordered_map>
23#include <utility>
24#include <vector>
25
26#include "palimpsest/Value.h"
28#include "palimpsest/json/write.h"
29#include "palimpsest/mpack/Writer.h"
30#include "palimpsest/mpack/read.h"
31#include "palimpsest/mpack/write.h"
32
34namespace palimpsest {
35
37
97 public:
99 Dictionary() = default;
100
102 Dictionary(const Dictionary &) = delete;
103
105 Dictionary &operator=(const Dictionary &) = delete;
106
108 Dictionary(Dictionary &&) = default;
109
112
118 ~Dictionary() = default;
119
121 bool is_map() const noexcept { return (value_.buffer == nullptr); }
122
124 bool is_empty() const noexcept { return (is_map() && map_.size() < 1); }
125
127 bool is_value() const noexcept { return (value_.buffer != nullptr); }
128
134 bool has(const std::string &key) const noexcept {
135 return (map_.find(key) != map_.end());
136 }
137
155 std::vector<std::string> keys() const noexcept;
156
180 std::vector<std::pair<std::string, std::reference_wrapper<const Dictionary>>>
181 items() const noexcept;
182
204 std::vector<std::reference_wrapper<const Dictionary>> values() const noexcept;
205
207 unsigned size() const noexcept { return map_.size(); }
208
216 template <typename T>
217 T &as() {
218 if (!this->is_value()) {
219 throw TypeError(__FILE__, __LINE__, "Object is not a value.");
220 }
221 return value_.get_reference<T>();
222 }
223
230 template <typename T>
231 const T &as() const {
232 if (!this->is_value()) {
233 throw TypeError(__FILE__, __LINE__, "Object is not a value.");
234 }
235 return const_cast<const T &>(value_.get_reference<T>());
236 }
237
254 template <typename T>
255 T &get(const std::string &key) {
256 return const_cast<T &>(get_<T>(key));
257 }
258
273 template <typename T>
274 const T &get(const std::string &key) const {
275 return get_<T>(key);
276 }
277
296 template <typename T>
297 const T &get(const std::string &key, const T &default_value) const {
298 auto it = map_.find(key);
299 if (it != map_.end()) {
300 if (it->second->is_map()) {
301 throw TypeError(__FILE__, __LINE__,
302 "Object at key \"" + key +
303 "\" is a dictionary, cannot get a single value "
304 "from it. Did you "
305 "mean to use operator()?");
306 }
307 try {
308 return it->second->value_.get_reference<T>();
309 } catch (const TypeError &e) {
310 throw TypeError(
311 __FILE__, __LINE__,
312 "Object for key \"" + key +
313 "\" does not have the same type as the stored type. Stored " +
314 it->second->value_.type_name() + " but requested " +
315 typeid(T).name() + ".");
316 }
317 }
318 return default_value;
319 }
320
336 template <typename T, typename... ArgsT, typename... Args>
337 T &insert(const std::string &key, Args &&...args) {
338 if (this->is_value()) {
339 throw TypeError(__FILE__, __LINE__,
340 "Cannot insert at key \"" + key +
341 "\" in non-dictionary object of type \"" +
342 value_.type_name() + "\".");
343 }
344 auto &child = this->operator()(key);
345 if (!child.is_empty()) {
346 spdlog::warn(
347 "[Dictionary::insert] Key \"{}\" already exists. Returning existing "
348 "value rather than creating a new one.",
349 key);
350 return get<T>(key);
351 }
352 child.value_.allocate<T>();
353 new (child.value_.buffer.get()) T(std::forward<Args>(args)...);
354 T &ret = child.value_.setup<T, ArgsT...>();
355 return ret;
356 }
357
384 template <typename T>
385 T &setdefault(const std::string &key, const T &default_value) {
386 if (this->is_value()) {
387 throw TypeError(__FILE__, __LINE__,
388 "Cannot insert at key \"" + key +
389 "\" in non-dictionary object of type \"" +
390 value_.type_name() + "\".");
391 }
392 auto it = map_.find(key);
393 if (it != map_.end()) {
394 if (it->second->is_map()) {
395 throw TypeError(__FILE__, __LINE__,
396 "Object at key \"" + key +
397 "\" is a dictionary, cannot get a single value "
398 "from it. Did you "
399 "mean to use operator()?");
400 }
401 try {
402 return it->second->value_.get_reference<T>();
403 } catch (const TypeError &e) {
404 throw TypeError(
405 __FILE__, __LINE__,
406 "Object for key \"" + key +
407 "\" does not have the same type as the stored type. Stored " +
408 it->second->value_.type_name() + " but requested " +
409 typeid(T).name() + ".");
410 }
411 }
412 auto &child = this->operator()(key);
413 child.value_.allocate<T>();
414 new (child.value_.buffer.get()) T(default_value);
415 T &ret = child.value_.setup<T>();
416 return ret;
417 }
418
429 template <typename T>
430 Dictionary &operator=(const T &new_value) {
431 if (this->is_map()) {
432 clear();
433 }
434 if (this->is_empty()) {
435 become<T>(new_value);
436 return *this;
437 }
438 auto &internal_value = value_.get_reference<T>();
439 internal_value = new_value;
440 return *this;
441 }
442
452 Dictionary &operator=(const char *c_string) {
453 return operator= <std::string>(std::string(c_string));
454 }
455
460 void remove(const std::string &key) noexcept;
461
484 template <typename T>
485 T pop(const std::string &key) {
486 const T value = get<T>(key);
487 remove(key);
488 return value;
489 }
490
514 template <typename T>
515 T pop(const std::string &key, const T &default_value) {
516 auto it = map_.find(key);
517 if (it == map_.end()) {
518 return default_value;
519 }
520 if (it->second->is_map()) {
521 throw TypeError(__FILE__, __LINE__,
522 "Object at key \"" + key +
523 "\" is a dictionary, cannot pop a single value "
524 "from it.");
525 }
526 try {
527 const T value = it->second->value_.get_reference<T>();
528 remove(key);
529 return value;
530 } catch (const TypeError &e) {
531 throw TypeError(
532 __FILE__, __LINE__,
533 "Object for key \"" + key +
534 "\" does not have the same type as the requested type. Stored " +
535 it->second->value_.type_name() + " but requested " +
536 typeid(T).name() + ".");
537 }
538 }
539
564 std::pair<std::string, Dictionary> popitem();
565
578 void clear() noexcept;
579
607 Dictionary &operator()(const std::string &key);
608
623 const Dictionary &operator()(const std::string &key) const;
624
643 static Dictionary deepcopy(const Dictionary &other);
644
664 template <typename Container, typename T>
665 static Dictionary fromkeys(const Container &keys, const T &value) {
666 Dictionary result;
667 for (const auto &key : keys) {
668 result(key) = value;
669 }
670 return result;
671 }
672
692 template <typename Container>
693 static Dictionary fromkeys(const Container &keys) {
694 Dictionary result;
695 for (const auto &key : keys) {
696 result(key); // Create empty dictionary for each key
697 }
698 return result;
699 }
700
707 size_t serialize(std::vector<char> &buffer) const;
708
713 void write(const std::string &filename) const;
714
719 void read(const std::string &filename);
720
743 void update(const Dictionary &other);
744
753 void deserialize(const char *data, size_t size);
754
767 Dictionary difference(const Dictionary &other) const;
768
770 operator bool &() { return this->as<bool>(); }
771
773 operator const bool &() const { return this->as<bool>(); }
774
776 operator int8_t &() { return this->as<int8_t>(); }
777
779 operator const int8_t &() const { return this->as<int8_t>(); }
780
782 operator int16_t &() { return this->as<int16_t>(); }
783
785 operator const int16_t &() const { return this->as<int16_t>(); }
786
788 operator int32_t &() { return this->as<int32_t>(); }
789
791 operator const int32_t &() const { return this->as<int32_t>(); }
792
794 operator int64_t &() { return this->as<int64_t>(); }
795
797 operator const int64_t &() const { return this->as<int64_t>(); }
798
800 operator uint8_t &() { return this->as<uint8_t>(); }
801
803 operator const uint8_t &() const { return this->as<uint8_t>(); }
804
806 operator uint16_t &() { return this->as<uint16_t>(); }
807
809 operator const uint16_t &() const { return this->as<uint16_t>(); }
810
812 operator uint32_t &() { return this->as<uint32_t>(); }
813
815 operator const uint32_t &() const { return this->as<uint32_t>(); }
816
818 operator uint64_t &() { return this->as<uint64_t>(); }
819
821 operator const uint64_t &() const { return this->as<uint64_t>(); }
822
824 operator float &() { return this->as<float>(); }
825
827 operator const float &() const { return this->as<float>(); }
828
830 operator double &() { return this->as<double>(); }
831
833 operator const double &() const { return this->as<double>(); }
834
836 operator std::string &() { return this->as<std::string>(); }
837
839 operator const std::string &() const { return this->as<std::string>(); }
840
842 operator Eigen::Vector2d &() { return this->as<Eigen::Vector2d>(); }
843
845 operator const Eigen::Vector2d &() const {
846 return this->as<Eigen::Vector2d>();
847 }
848
850 operator Eigen::Vector3d &() { return this->as<Eigen::Vector3d>(); }
851
853 operator const Eigen::Vector3d &() const {
854 return this->as<Eigen::Vector3d>();
855 }
856
858 operator Eigen::VectorXd &() { return this->as<Eigen::VectorXd>(); }
859
861 operator const Eigen::VectorXd &() const {
862 return this->as<Eigen::VectorXd>();
863 }
864
866 operator Eigen::Quaterniond &() { return this->as<Eigen::Quaterniond>(); }
867
869 operator const Eigen::Quaterniond &() const {
870 return this->as<Eigen::Quaterniond>();
871 }
872
874 operator Eigen::Matrix3d &() { return this->as<Eigen::Matrix3d>(); }
875
877 operator const Eigen::Matrix3d &() const {
878 return this->as<Eigen::Matrix3d>();
879 }
880
887 friend std::ostream &operator<<(std::ostream &stream, const Dictionary &dict);
888
889 protected:
890 template <typename T, typename... ArgsT, typename... Args>
891 void become(Args &&...args) {
892 assert(this->is_empty());
893 value_.allocate<T>();
894 new (value_.buffer.get()) T(std::forward<Args>(args)...);
895 value_.setup<T, ArgsT...>();
896 }
897
898 private:
906 void deserialize_(mpack_node_t node);
907
916 template <typename T>
917 const T &get_(const std::string &key) const {
918 const auto &child_value = get_child_value_(key);
919 try {
920 return child_value.get_reference<T>();
921 } catch (const TypeError &e) {
922 throw TypeError(__FILE__, __LINE__,
923 "Object at key \"" + key + "\" has type \"" +
924 child_value.type_name() +
925 "\", but is being cast to type \"" +
926 typeid(T).name() + "\".");
927 }
928 }
929
938 const Value &get_child_value_(const std::string &key) const;
939
947 void insert_at_key_(const std::string &key, const mpack_node_t &value);
948
953 void serialize_(mpack::Writer &writer) const;
954
955 protected:
958
960 std::unordered_map<std::string, std::unique_ptr<Dictionary>> map_;
961};
962
963} // namespace palimpsest
964
965#include "palimpsest/internal/fmt_formatter.h"
Dictionary of values and sub-dictionaries.
Definition: Dictionary.h:96
Dictionary(Dictionary &&)=default
Default move constructor.
Value value_
Internal value, used if we are a value.
Definition: Dictionary.h:957
std::unordered_map< std::string, std::unique_ptr< Dictionary > > map_
Key-value map, used if we are a map.
Definition: Dictionary.h:960
Dictionary & operator=(const T &new_value)
Assign value directly.
Definition: Dictionary.h:430
void read(const std::string &filename)
Update dictionary from a MessagePack binary file.
Definition: Dictionary.cpp:334
bool is_map() const noexcept
We are a (potentially empty) map if and only if the value is empty.
Definition: Dictionary.h:121
void deserialize(const char *data, size_t size)
Update dictionary from raw MessagePack data.
Definition: Dictionary.cpp:49
unsigned size() const noexcept
Return the number of keys in the dictionary.
Definition: Dictionary.h:207
std::pair< std::string, Dictionary > popitem()
Remove and return a (key, value) pair from the dictionary.
Definition: Dictionary.cpp:276
const T & get(const std::string &key, const T &default_value) const
Get object at a given key if it exists, or a default value otherwise.
Definition: Dictionary.h:297
Dictionary & operator=(Dictionary &&)=default
Default move assignment operator.
T pop(const std::string &key)
Remove a key-value pair from the dictionary and return its value.
Definition: Dictionary.h:485
T & as()
Get reference to the internal value.
Definition: Dictionary.h:217
bool is_empty() const noexcept
We are empty if and only if we are a dictionary with no element.
Definition: Dictionary.h:124
Dictionary & operator=(const char *c_string)
Assignment operator for C-style strings.
Definition: Dictionary.h:452
static Dictionary deepcopy(const Dictionary &other)
Create a deep copy of an existing dictionary.
Definition: Dictionary.cpp:43
T & insert(const std::string &key, Args &&...args)
Create an object at a given key and return a reference to it.
Definition: Dictionary.h:337
Dictionary & operator=(const Dictionary &)=delete
No copy assignment operator.
Dictionary()=default
Default constructor.
const T & get(const std::string &key) const
Const variant of get.
Definition: Dictionary.h:274
void remove(const std::string &key) noexcept
Remove a key-value pair from the dictionary.
Definition: Dictionary.cpp:299
T & get(const std::string &key)
Get reference to the object at a given key.
Definition: Dictionary.h:255
Dictionary(const Dictionary &)=delete
No copy constructor.
const T & as() const
Const variant of as.
Definition: Dictionary.h:231
void update(const Dictionary &other)
Update dictionary from another dictionary.
Definition: Dictionary.cpp:411
void become(Args &&...args)
Definition: Dictionary.h:891
bool has(const std::string &key) const noexcept
Check whether a key is in the dictionary.
Definition: Dictionary.h:134
~Dictionary()=default
Default destructor.
T & setdefault(const std::string &key, const T &default_value)
If key is in the dictionary, return its value.
Definition: Dictionary.h:385
Dictionary & operator()(const std::string &key)
Return a reference to the dictionary at key, performing an insertion if such a key does not already e...
Definition: Dictionary.cpp:308
size_t serialize(std::vector< char > &buffer) const
Serialize to raw MessagePack data.
Definition: Dictionary.cpp:355
std::vector< std::string > keys() const noexcept
Return the list of keys of the dictionary.
Definition: Dictionary.cpp:246
T pop(const std::string &key, const T &default_value)
Remove a key-value pair from the dictionary and return its value, or return a default value if the ke...
Definition: Dictionary.h:515
std::vector< std::pair< std::string, std::reference_wrapper< const Dictionary > > > items() const noexcept
Return an iterable view of the dictionary's (key, value) pairs.
Definition: Dictionary.cpp:256
static Dictionary fromkeys(const Container &keys)
Create a new dictionary with keys from an iterable container and all values set to empty dictionaries...
Definition: Dictionary.h:693
friend std::ostream & operator<<(std::ostream &stream, const Dictionary &dict)
Output stream operator for printing.
Definition: Dictionary.cpp:388
void write(const std::string &filename) const
Write MessagePack serialization to a binary file.
Definition: Dictionary.cpp:345
void clear() noexcept
Remove all entries from the dictionary.
Definition: Dictionary.cpp:38
Dictionary difference(const Dictionary &other) const
Compute the difference between this dictionary and another.
Definition: Dictionary.cpp:190
std::vector< std::reference_wrapper< const Dictionary > > values() const noexcept
Return an iterable view of the dictionary's values.
Definition: Dictionary.cpp:266
bool is_value() const noexcept
We are a value if and only if the internal value is non-empty.
Definition: Dictionary.h:127
static Dictionary fromkeys(const Container &keys, const T &value)
Create a new dictionary with keys from an iterable container and all values set to the same value.
Definition: Dictionary.h:665
Internal wrapper around an object and its type information.
Definition: Value.h:38
T & setup()
Allocate object and register internal functions.
Definition: Value.h:113
void allocate()
Allocate the internal buffer.
Definition: Value.h:82
T & get_reference() const
Cast value to its object's type after checking that it matches T.
Definition: Value.h:155
std::unique_ptr< uint8_t[]> buffer
Internal buffer that holds the actual object.
Definition: Value.h:168
const char *(* type_name)()
Function returning the name of the object's type.
Definition: Value.h:171
Requested type doesn't match the one already in the dictionary.
Definition: TypeError.h:13
Main library namespace.
Definition: Dictionary.h:34