LSST Applications g180d380827+0f66a164bb,g2079a07aa2+86d27d4dc4,g2305ad1205+7d304bc7a0,g29320951ab+500695df56,g2bbee38e9b+0e5473021a,g337abbeb29+0e5473021a,g33d1c0ed96+0e5473021a,g3a166c0a6a+0e5473021a,g3ddfee87b4+e42ea45bea,g48712c4677+36a86eeaa5,g487adcacf7+2dd8f347ac,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+c70619cc9d,g5a732f18d5+53520f316c,g5ea96fc03c+341ea1ce94,g64a986408d+f7cd9c7162,g858d7b2824+f7cd9c7162,g8a8a8dda67+585e252eca,g99cad8db69+469ab8c039,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,gb0e22166c9+60f28cb32d,gba4ed39666+c2a2e4ac27,gbb8dafda3b+c92fc63c7e,gbd866b1f37+f7cd9c7162,gc120e1dc64+02c66aa596,gc28159a63d+0e5473021a,gc3e9b769f7+b0068a2d9f,gcf0d15dbbd+e42ea45bea,gdaeeff99f8+f9a426f77a,ge6526c86ff+84383d05b3,ge79ae78c31+0e5473021a,gee10cc3b42+585e252eca,gff1a9f87cc+f7cd9c7162,w.2024.17
LSST Data Management Base Package
Loading...
Searching...
No Matches
GenericMap.h
Go to the documentation of this file.
1// -*- LSST-C++ -*-
2/*
3 * This file is part of afw.
4 *
5 * Developed for the LSST Data Management System.
6 * This product includes software developed by the LSST Project
7 * (https://www.lsst.org).
8 * See the COPYRIGHT file at the top-level directory of this distribution
9 * for details of code ownership.
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
25#ifndef LSST_AFW_TYPEHANDLING_GENERICMAP_H
26#define LSST_AFW_TYPEHANDLING_GENERICMAP_H
27
28#include <algorithm>
29#include <functional>
30#include <memory>
31#include <sstream>
32#include <string>
33#include <type_traits>
34#include <utility>
35#include <vector>
36#include <variant>
37
38#include "lsst/pex/exceptions.h"
44
45namespace lsst {
46namespace afw {
47namespace typehandling {
48
77// TODO: correctly handling const vs. non-const keys should be possible in C++17 with std::variant
78template <typename K>
80public:
81 using key_type = K;
84
85 virtual ~GenericMap() noexcept = default;
86
108 template <typename T, typename std::enable_if_t<!detail::IS_SMART_PTR<T>, int> = 0>
109 T& at(Key<K, T> const& key) {
110 // Both casts are safe; see Effective C++, Item 3
111 return const_cast<T&>(static_cast<const GenericMap&>(*this).at(key));
112 }
113
114 template <typename T, typename std::enable_if_t<!detail::IS_SMART_PTR<T>, int> = 0>
115 T const& at(Key<K, T> const& key) const {
116 // Delegate to private methods to hide further special-casing of T
117 return _at(key);
118 }
119
120 template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
122 // Delegate to private methods to hide further special-casing of T
123 return _at(key);
124 }
125
129 virtual size_type size() const noexcept = 0;
130
132 virtual bool empty() const noexcept = 0;
133
141 virtual size_type max_size() const noexcept = 0;
142
156 template <typename T>
157 size_type count(Key<K, T> const& key) const {
158 return contains(key) ? 1 : 0;
159 }
160
173 virtual bool contains(K const& key) const = 0;
174
193 template <typename T>
194 bool contains(Key<K, T> const& key) const {
195 // Delegate to private methods to hide special-casing of T
196 return _contains(key);
197 }
198
213 virtual std::vector<K> const& keys() const noexcept = 0;
214
229 virtual bool operator==(GenericMap const& other) const noexcept {
230 auto keys1 = this->keys();
231 auto keys2 = other.keys();
232 if (!std::is_permutation(keys1.begin(), keys1.end(), keys2.begin(), keys2.end())) {
233 return false;
234 }
235 for (K const& key : keys1) {
236 // Can't use std::variant::operator== here because
237 // std::reference_wrapper isn't equality-comparable.
238 if (!detail::refwrap_equals()(this->unsafeLookup(key), other.unsafeLookup(key))) {
239 return false;
240 }
241 }
242 return true;
243 }
244
245 bool operator!=(GenericMap const& other) const { return !(*this == other); }
246
308 template <class Visitor>
309 auto apply(Visitor&& visitor) const {
310 // Delegate to private methods to hide special-casing of Visitor
311 return _apply(visitor);
312 }
313
330 template <class Visitor>
331 auto apply(Visitor&& visitor) {
332 // Delegate to private methods to hide special-casing of Visitor
333 return _apply(visitor);
334 }
335
336private:
337 // Transformations between value/reference/ref-to-const versions of internal variant type, to let
338 // the set of value types supported by GenericMap be defined only once
339 // Icky TMP, but I can't find another way to get at the template arguments for variant :(
340 // Methods have no definition but can't be deleted without breaking type definitions below
341
343 template <typename... Types>
344 static std::variant<std::reference_wrapper<Types>...> _typeToRef(
345 std::variant<Types...> const&) noexcept;
346 template <typename... Types>
347 static std::variant<std::reference_wrapper<std::add_const_t<Types>>...> _typeToConstRef(
348 std::variant<Types...> const&) noexcept;
350
351protected:
357 // Use int, long, long long instead of int32_t, int64_t because C++ doesn't
358 // consider signed integers of the same length but different names equivalent
359 using StorableType = std::variant<bool, int, long, long long, float, double, std::string,
361
368 // this mouthful is shorter than the equivalent expression with result_of
369 using ConstValueReference = decltype(_typeToConstRef(std::declval<StorableType>()));
370 using ValueReference = decltype(_typeToRef(std::declval<StorableType>()));
371
389 virtual ConstValueReference unsafeLookup(K key) const = 0;
390
392 ConstValueReference constRef = static_cast<const GenericMap&>(*this).unsafeLookup(key);
393 return std::visit(
394 [](auto const & v) { return ValueReference(detail::refwrap_const_cast(v)); },
395 constRef
396 );
397 }
398
401private:
402 // Neither Storable nor shared_ptr<Storable>
403 template <typename T,
404 typename std::enable_if_t<
406 T const& _at(Key<K, T> const& key) const {
407 static_assert(!std::is_const<T>::value,
408 "Due to implementation constraints, const keys are not supported.");
409 auto foo = unsafeLookup(key.getId());
410 if (std::holds_alternative<std::reference_wrapper<T const>>(foo)) {
411 return std::get<std::reference_wrapper<T const>>(foo);
412 } else {
413 std::stringstream message;
414 message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
416 }
417 }
418
419 // Storable and its subclasses
420 template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
421 T const& _at(Key<K, T> const& key) const {
422 static_assert(!std::is_const<T>::value,
423 "Due to implementation constraints, const keys are not supported.");
424 auto foo = unsafeLookup(key.getId());
425 if (std::holds_alternative<std::reference_wrapper<PolymorphicValue const>>(foo)) {
426 Storable const& value = std::get<std::reference_wrapper<PolymorphicValue const>>(foo).get();
427 T const* typedPointer = dynamic_cast<T const*>(&value);
428 if (typedPointer != nullptr) {
429 return *typedPointer;
430 } else {
431 std::stringstream message;
432 message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
433 throw LSST_EXCEPT(pex::exceptions::OutOfRangeError, message.str());
434 }
435 } else {
436 std::stringstream message;
437 message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
438 throw LSST_EXCEPT(pex::exceptions::OutOfRangeError, message.str());
439 }
440 }
441
442 // shared_ptr<Storable>
443 template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
444 std::shared_ptr<T> _at(Key<K, std::shared_ptr<T>> const& key) const {
445 static_assert(std::is_const<T>::value,
446 "Due to implementation constraints, pointers to non-const are not supported.");
447 auto foo = unsafeLookup(key.getId());
448 if (std::holds_alternative<std::reference_wrapper<std::shared_ptr<Storable const> const>>(foo)) {
449 auto pointer = std::get<std::reference_wrapper<std::shared_ptr<Storable const> const>>(foo).get();
450 if (pointer == nullptr) { // dynamic_cast not helpful
451 return nullptr;
452 }
453
454 std::shared_ptr<T> typedPointer = std::dynamic_pointer_cast<T>(pointer);
455 // shared_ptr can be empty without being null. dynamic_pointer_cast
456 // only promises result of failed cast is empty, so test for that
457 if (typedPointer.use_count() > 0) {
458 return typedPointer;
459 } else {
460 std::stringstream message;
461 message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
462 throw LSST_EXCEPT(pex::exceptions::OutOfRangeError, message.str());
463 }
464 } else {
465 std::stringstream message;
466 message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
467 throw LSST_EXCEPT(pex::exceptions::OutOfRangeError, message.str());
468 }
469 }
470
471 // Neither Storable nor shared_ptr<Storable>
472 template <typename T,
473 typename std::enable_if_t<
475 bool _contains(Key<K, T> const& key) const {
476 // Avoid actually getting and casting an object, if at all possible
477 if (!contains(key.getId())) {
478 return false;
479 }
480
481 auto foo = unsafeLookup(key.getId());
482 return std::holds_alternative<std::reference_wrapper<T const>>(foo);
483 }
484
485 // Storable and its subclasses
486 template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
487 bool _contains(Key<K, T> const& key) const {
488 // Avoid actually getting and casting an object, if at all possible
489 if (!contains(key.getId())) {
490 return false;
491 }
492
493 auto foo = unsafeLookup(key.getId());
494 auto refwrap = std::get_if<std::reference_wrapper<PolymorphicValue const>>(&foo);
495 if (refwrap) {
496 Storable const & value = refwrap->get();
497 auto asT = dynamic_cast<T const*>(&value);
498 return asT != nullptr;
499 } else {
500 return false;
501 }
502 }
503
504 // shared_ptr<Storable>
505 template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
506 bool _contains(Key<K, std::shared_ptr<T>> const& key) const {
507 static_assert(std::is_const<T>::value,
508 "Due to implementation constraints, pointers to non-const are not supported.");
509 // Avoid actually getting and casting an object, if at all possible
510 if (!contains(key.getId())) {
511 return false;
512 }
513
514 auto foo = unsafeLookup(key.getId());
515 if (std::holds_alternative<std::reference_wrapper<std::shared_ptr<Storable const> const>>(foo)) {
516 auto pointer = std::get<std::reference_wrapper<std::shared_ptr<Storable const> const>>(foo).get();
517 if (pointer == nullptr) { // Can't confirm type with dynamic_cast
518 return true;
519 }
520 std::shared_ptr<T> typedPointer = std::dynamic_pointer_cast<T>(pointer);
521 // shared_ptr can be empty without being null. dynamic_pointer_cast
522 // only promises result of failed cast is empty, so test for that
523 return typedPointer.use_count() > 0;
524 } else {
525 return false;
526 }
527 }
528
529 // Type alias to properly handle Visitor output
530 // Assume that each operator() has the same return type; variant will enforce it
532 template <class Visitor>
533 using _VisitorResult = std::result_of_t<Visitor && (K&&, bool&)>;
535
536 // No return value, const GenericMap
537 template <class Visitor, typename std::enable_if_t<std::is_void<_VisitorResult<Visitor>>::value, int> = 0>
538 void _apply(Visitor&& visitor) const {
539 auto wrapped = detail::make_refwrap_visitor(std::forward<Visitor>(visitor));
540 for (K const& key : keys()) {
541 std::variant<K> varKey = key;
542 std::visit(wrapped, varKey, unsafeLookup(key));
543 }
544 }
545
546 // Return value, const GenericMap
547 template <class Visitor,
548 typename std::enable_if_t<!std::is_void<_VisitorResult<Visitor>>::value, int> = 0>
549 auto _apply(Visitor&& visitor) const {
551 results.reserve(size());
552 auto wrapped = detail::make_refwrap_visitor(std::forward<Visitor>(visitor));
553 for (K const& key : keys()) {
554 std::variant<K> varKey = key;
555 results.emplace_back(std::visit(wrapped, varKey, unsafeLookup(key)));
556 }
557 return results;
558 }
559
560 // No return value, non-const GenericMap
561 template <class Visitor, typename std::enable_if_t<std::is_void<_VisitorResult<Visitor>>::value, int> = 0>
562 void _apply(Visitor&& visitor) {
563 auto wrapped = detail::make_refwrap_visitor(std::forward<Visitor>(visitor));
564 for (K const& key : keys()) {
565 std::variant<K> varKey = key;
566 std::visit(wrapped, varKey, unsafeLookup(key));
567 }
568 }
569
570 // Return value, non-const GenericMap
571 template <class Visitor,
572 typename std::enable_if_t<!std::is_void<_VisitorResult<Visitor>>::value, int> = 0>
573 auto _apply(Visitor&& visitor) {
575 results.reserve(size());
576 auto wrapped = detail::make_refwrap_visitor(std::forward<Visitor>(visitor));
577
578 for (K const& key : keys()) {
579 std::variant<K> varKey = key;
580 results.emplace_back(std::visit(wrapped, varKey, unsafeLookup(key)));
581 }
582 return results;
583 }
584};
585
596template <typename K>
597class MutableGenericMap : public GenericMap<K> {
598protected:
599 using typename GenericMap<K>::StorableType;
600
601public:
602 virtual ~MutableGenericMap() noexcept = default;
603
609 virtual void clear() noexcept = 0;
610
629 template <typename T>
630 bool insert(Key<K, T> const& key, T const& value) {
631 if (this->contains(key.getId())) {
632 return false;
633 }
634
635 return unsafeInsert(key.getId(), StorableType(value));
636 }
637
653 template <typename T>
654 std::pair<Key<K, T>, bool> insert(K const& key, T const& value) {
655 auto strongKey = makeKey<T>(key);
656 // Construct return value in advance, so that exception from copying/moving Key is atomic
657 auto result = make_pair(strongKey, false);
658 result.second = insert(strongKey, value);
659 return result;
660 }
661
675 template <typename T>
676 bool erase(Key<K, T> const& key) {
677 if (this->contains(key)) {
678 return unsafeErase(key.getId());
679 } else {
680 return false;
681 }
682 }
683
684protected:
697 virtual bool unsafeInsert(K key, StorableType&& value) = 0;
698
708 virtual bool unsafeErase(K key) = 0;
709};
710
711} // namespace typehandling
712} // namespace afw
713} // namespace lsst
714
715#endif
py::object result
Definition _schema.cc:429
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
Interface for a heterogeneous map.
Definition GenericMap.h:79
T & at(Key< K, T > const &key)
Return a reference to the mapped value of the element with key equal to key.
Definition GenericMap.h:109
size_type count(Key< K, T > const &key) const
Return the number of elements mapped to the specified key.
Definition GenericMap.h:157
T const & at(Key< K, T > const &key) const
Return a reference to the mapped value of the element with key equal to key.
Definition GenericMap.h:115
std::variant< bool, int, long, long long, float, double, std::string, PolymorphicValue, std::shared_ptr< Storable const > > StorableType
The types that can be stored in a map.
Definition GenericMap.h:359
std::shared_ptr< T > at(Key< K, std::shared_ptr< T > > const &key) const
Return a reference to the mapped value of the element with key equal to key.
Definition GenericMap.h:121
virtual ConstValueReference unsafeLookup(K key) const =0
Return a reference to the mapped value of the element with key equal to key.
bool contains(Key< K, T > const &key) const
Return true if this map contains a mapping for the specified key.
Definition GenericMap.h:194
decltype(_typeToRef(std::declval< StorableType >())) ValueReference
A type-agnostic reference to the value stored inside the map.
Definition GenericMap.h:370
virtual bool empty() const noexcept=0
Return true if this map contains no key-value pairs.
auto apply(Visitor &&visitor)
Apply a modifying operation to each key-value pair in the map.
Definition GenericMap.h:331
virtual std::vector< K > const & keys() const noexcept=0
Return the set of all keys, without type information.
decltype(_typeToConstRef(std::declval< StorableType >())) ConstValueReference
A type-agnostic reference to the value stored inside the map.
Definition GenericMap.h:369
virtual size_type max_size() const noexcept=0
Return the maximum number of elements the container is able to hold due to system or library implemen...
virtual size_type size() const noexcept=0
Return the number of key-value pairs in the map.
auto apply(Visitor &&visitor) const
Apply an operation to each key-value pair in the map.
Definition GenericMap.h:309
ValueReference unsafeLookup(K key)
Return a reference to the mapped value of the element with key equal to key.
Definition GenericMap.h:391
virtual ~GenericMap() noexcept=default
virtual bool contains(K const &key) const =0
Return true if this map contains a mapping whose key has the specified label.
bool operator!=(GenericMap const &other) const
Test for map equality.
Definition GenericMap.h:245
Key for type-safe lookup in a GenericMap.
Definition Key.h:52
constexpr K const & getId() const noexcept
Return the identifier of this field.
Definition Key.h:105
Interface for a GenericMap that allows element addition and removal.
Definition GenericMap.h:597
virtual bool unsafeInsert(K key, StorableType &&value)=0
Create a new mapping with key equal to key and value equal to value.
virtual bool unsafeErase(K key)=0
Remove the mapping for a key from this map, if it exists.
virtual ~MutableGenericMap() noexcept=default
bool insert(Key< K, T > const &key, T const &value)
Insert an element into the map, if the map doesn't already contain a mapping with the same or a confl...
Definition GenericMap.h:630
std::pair< Key< K, T >, bool > insert(K const &key, T const &value)
Insert an element into the map, if the map doesn't already contain a mapping with a conflicting key.
Definition GenericMap.h:654
bool erase(Key< K, T > const &key)
Remove the mapping for a key from this map, if it exists.
Definition GenericMap.h:676
virtual void clear() noexcept=0
Remove all of the mappings from this map.
Container that passes Storable objects by value while preserving type.
Reports attempts to access elements outside a valid range of indices.
Definition Runtime.h:89
T is_permutation(T... args)
std::reference_wrapper< T > refwrap_const_cast(std::reference_wrapper< T const > const &r)
refwrap_visitor< F > make_refwrap_visitor(F &&func)
STL namespace.
T str(T... args)