LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
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 
45 namespace lsst {
46 namespace afw {
47 namespace typehandling {
48 
77 // TODO: correctly handling const vs. non-const keys should be possible in C++17 with std::variant
78 template <typename K>
79 class GenericMap {
80 public:
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>
121  std::shared_ptr<T> at(Key<K, std::shared_ptr<T>> const& key) const {
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 
336 private:
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 
351 protected:
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 
401 private:
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 
596 template <typename K>
597 class MutableGenericMap : public GenericMap<K> {
598 protected:
599  using typename GenericMap<K>::StorableType;
600 
601 public:
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 
684 protected:
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
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
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.
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
auto apply(Visitor &&visitor)
Apply a modifying operation to each key-value pair in the map.
Definition: GenericMap.h:331
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...
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
virtual std::vector< K > const & keys() const noexcept=0
Return the set of all keys, without type information.
virtual size_type size() const noexcept=0
Return the number of key-value pairs in the map.
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:360
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
bool erase(Key< K, T > const &key)
Remove the mapping for a key from this map, if it exists.
Definition: GenericMap.h:676
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
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 emplace_back(T... args)
T is_permutation(T... args)
refwrap_visitor< F > make_refwrap_visitor(F &&func)
std::reference_wrapper< T > refwrap_const_cast(std::reference_wrapper< T const > const &r)
Definition: refwrap_utils.h:64
A base class for image defects.
STL namespace.
T reserve(T... args)
T str(T... args)