LSST Applications  21.0.0+04719a4bac,21.0.0-1-ga51b5d4+f5e6047307,21.0.0-11-g2b59f77+a9c1acf22d,21.0.0-11-ga42c5b2+86977b0b17,21.0.0-12-gf4ce030+76814010d2,21.0.0-13-g1721dae+760e7a6536,21.0.0-13-g3a573fe+768d78a30a,21.0.0-15-g5a7caf0+f21cbc5713,21.0.0-16-g0fb55c1+b60e2d390c,21.0.0-19-g4cded4ca+71a93a33c0,21.0.0-2-g103fe59+bb20972958,21.0.0-2-g45278ab+04719a4bac,21.0.0-2-g5242d73+3ad5d60fb1,21.0.0-2-g7f82c8f+8babb168e8,21.0.0-2-g8f08a60+06509c8b61,21.0.0-2-g8faa9b5+616205b9df,21.0.0-2-ga326454+8babb168e8,21.0.0-2-gde069b7+5e4aea9c2f,21.0.0-2-gecfae73+1d3a86e577,21.0.0-2-gfc62afb+3ad5d60fb1,21.0.0-25-g1d57be3cd+e73869a214,21.0.0-3-g357aad2+ed88757d29,21.0.0-3-g4a4ce7f+3ad5d60fb1,21.0.0-3-g4be5c26+3ad5d60fb1,21.0.0-3-g65f322c+e0b24896a3,21.0.0-3-g7d9da8d+616205b9df,21.0.0-3-ge02ed75+a9c1acf22d,21.0.0-4-g591bb35+a9c1acf22d,21.0.0-4-g65b4814+b60e2d390c,21.0.0-4-gccdca77+0de219a2bc,21.0.0-4-ge8a399c+6c55c39e83,21.0.0-5-gd00fb1e+05fce91b99,21.0.0-6-gc675373+3ad5d60fb1,21.0.0-64-g1122c245+4fb2b8f86e,21.0.0-7-g04766d7+cd19d05db2,21.0.0-7-gdf92d54+04719a4bac,21.0.0-8-g5674e7b+d1bd76f71f,master-gac4afde19b+a9c1acf22d,w.2021.13
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 <memory>
30 #include <sstream>
31 #include <string>
32 #include <type_traits>
33 #include <utility>
34 #include <vector>
35 
36 #include "boost/variant.hpp"
37 
38 #include "lsst/pex/exceptions.h"
43 
44 namespace lsst {
45 namespace afw {
46 namespace typehandling {
47 
76 // TODO: correctly handling const vs. non-const keys should be possible in C++17 with std::variant
77 template <typename K>
78 class GenericMap {
79 public:
80  using key_type = K;
83 
84  virtual ~GenericMap() noexcept = default;
85 
107  template <typename T, typename std::enable_if_t<!detail::IS_SMART_PTR<T>, int> = 0>
108  T& at(Key<K, T> const& key) {
109  // Both casts are safe; see Effective C++, Item 3
110  return const_cast<T&>(static_cast<const GenericMap&>(*this).at(key));
111  }
112 
113  template <typename T, typename std::enable_if_t<!detail::IS_SMART_PTR<T>, int> = 0>
114  T const& at(Key<K, T> const& key) const {
115  // Delegate to private methods to hide further special-casing of T
116  return _at(key);
117  }
118 
119  template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
121  // Delegate to private methods to hide further special-casing of T
122  return _at(key);
123  }
124 
128  virtual size_type size() const noexcept = 0;
129 
131  virtual bool empty() const noexcept = 0;
132 
140  virtual size_type max_size() const noexcept = 0;
141 
155  template <typename T>
156  size_type count(Key<K, T> const& key) const {
157  return contains(key) ? 1 : 0;
158  }
159 
172  virtual bool contains(K const& key) const = 0;
173 
192  template <typename T>
193  bool contains(Key<K, T> const& key) const {
194  // Delegate to private methods to hide special-casing of T
195  return _contains(key);
196  }
197 
212  virtual std::vector<K> const& keys() const noexcept = 0;
213 
228  virtual bool operator==(GenericMap const& other) const noexcept {
229  auto keys1 = this->keys();
230  auto keys2 = other.keys();
231  if (!std::is_permutation(keys1.begin(), keys1.end(), keys2.begin(), keys2.end())) {
232  return false;
233  }
234  for (K const& key : keys1) {
235  if (this->unsafeLookup(key) != other.unsafeLookup(key)) {
236  return false;
237  }
238  }
239  return true;
240  }
241 
242  bool operator!=(GenericMap const& other) const { return !(*this == other); }
243 
305  template <class Visitor>
306  auto apply(Visitor&& visitor) const {
307  // Delegate to private methods to hide special-casing of Visitor
308  return _apply(visitor);
309  }
310 
327  template <class Visitor>
328  auto apply(Visitor&& visitor) {
329  // Delegate to private methods to hide special-casing of Visitor
330  return _apply(visitor);
331  }
332 
333 private:
334  // Transformations between value/reference/ref-to-const versions of internal variant type, to let
335  // the set of value types supported by GenericMap be defined only once
336  // Icky TMP, but I can't find another way to get at the template arguments for variant :(
337  // Methods have no definition but can't be deleted without breaking type definitions below
338 
340  // may need to use std::reference_wrapper when migrating to std::variant, but it confuses Boost
341  template <typename... Types>
342  static boost::variant<std::add_lvalue_reference_t<Types>...> _typeToRef(
343  boost::variant<Types...> const&) noexcept;
344  template <typename... Types>
345  static boost::variant<std::add_lvalue_reference_t<std::add_const_t<Types>>...> _typeToConstRef(
346  boost::variant<Types...> const&) noexcept;
348 
349 protected:
355  // Use int, long, long long instead of int32_t, int64_t because C++ doesn't
356  // consider signed integers of the same length but different names equivalent
357  using StorableType = boost::variant<bool, int, long, long long, float, double, std::string,
359 
366  // this mouthful is shorter than the equivalent expression with result_of
367  using ConstValueReference = decltype(_typeToConstRef(std::declval<StorableType>()));
368  using ValueReference = decltype(_typeToRef(std::declval<StorableType>()));
369 
387  virtual ConstValueReference unsafeLookup(K key) const = 0;
388 
390  ConstValueReference constRef = static_cast<const GenericMap&>(*this).unsafeLookup(key);
391  auto removeConst = [](auto const& value) -> ValueReference {
392  using NonConstRef = std::add_lvalue_reference_t<
393  std::remove_const_t<std::remove_reference_t<decltype(value)>>>;
394  // This cast is safe; see Effective C++, Item 3
395  return const_cast<NonConstRef>(value);
396  };
397  return boost::apply_visitor(removeConst, constRef);
398  }
399 
402 private:
403  // Neither Storable nor shared_ptr<Storable>
404  template <typename T,
405  typename std::enable_if_t<
407  T const& _at(Key<K, T> const& key) const {
408  static_assert(!std::is_const<T>::value,
409  "Due to implementation constraints, const keys are not supported.");
410  try {
411  auto foo = unsafeLookup(key.getId());
412  return boost::get<T const&>(foo);
413  } catch (boost::bad_get const&) {
414  std::stringstream message;
415  message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
417  }
418  }
419 
420  // Storable and its subclasses
421  template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
422  T const& _at(Key<K, T> const& key) const {
423  static_assert(!std::is_const<T>::value,
424  "Due to implementation constraints, const keys are not supported.");
425  try {
426  auto foo = unsafeLookup(key.getId());
427  // Don't use pointer-based get, because it won't work after migrating to std::variant
428  Storable const& value = boost::get<PolymorphicValue const&>(foo);
429  T const* typedPointer = dynamic_cast<T const*>(&value);
430  if (typedPointer != nullptr) {
431  return *typedPointer;
432  } else {
433  std::stringstream message;
434  message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
435  throw LSST_EXCEPT(pex::exceptions::OutOfRangeError, message.str());
436  }
437  } catch (boost::bad_get const&) {
438  std::stringstream message;
439  message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
440  throw LSST_EXCEPT(pex::exceptions::OutOfRangeError, message.str());
441  }
442  }
443 
444  // shared_ptr<Storable>
445  template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
446  std::shared_ptr<T> _at(Key<K, std::shared_ptr<T>> const& key) const {
447  static_assert(std::is_const<T>::value,
448  "Due to implementation constraints, pointers to non-const are not supported.");
449  try {
450  auto foo = unsafeLookup(key.getId());
451  auto pointer = boost::get<std::shared_ptr<Storable const> const&>(foo);
452  if (pointer == nullptr) { // dynamic_cast not helpful
453  return nullptr;
454  }
455 
456  std::shared_ptr<T> typedPointer = std::dynamic_pointer_cast<T>(pointer);
457  // shared_ptr can be empty without being null. dynamic_pointer_cast
458  // only promises result of failed cast is empty, so test for that
459  if (typedPointer.use_count() > 0) {
460  return typedPointer;
461  } else {
462  std::stringstream message;
463  message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
464  throw LSST_EXCEPT(pex::exceptions::OutOfRangeError, message.str());
465  }
466  } catch (boost::bad_get const&) {
467  std::stringstream message;
468  message << "Key " << key << " not found, but a key labeled " << key.getId() << " is present.";
469  throw LSST_EXCEPT(pex::exceptions::OutOfRangeError, message.str());
470  }
471  }
472 
473  // Neither Storable nor shared_ptr<Storable>
474  template <typename T,
475  typename std::enable_if_t<
477  bool _contains(Key<K, T> const& key) const {
478  // Avoid actually getting and casting an object, if at all possible
479  if (!contains(key.getId())) {
480  return false;
481  }
482 
483  auto foo = unsafeLookup(key.getId());
484  // boost::variant has no equivalent to std::holds_alternative
485  try {
486  boost::get<T const&>(foo);
487  return true;
488  } catch (boost::bad_get const&) {
489  return false;
490  }
491  }
492 
493  // Storable and its subclasses
494  template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
495  bool _contains(Key<K, T> const& key) const {
496  // Avoid actually getting and casting an object, if at all possible
497  if (!contains(key.getId())) {
498  return false;
499  }
500 
501  auto foo = unsafeLookup(key.getId());
502  try {
503  // Don't use pointer-based get, because it won't work after migrating to std::variant
504  Storable const& value = boost::get<PolymorphicValue const&>(foo);
505  auto asT = dynamic_cast<T const*>(&value);
506  return asT != nullptr;
507  } catch (boost::bad_get const&) {
508  return false;
509  }
510  }
511 
512  // shared_ptr<Storable>
513  template <typename T, typename std::enable_if_t<std::is_base_of<Storable, T>::value, int> = 0>
514  bool _contains(Key<K, std::shared_ptr<T>> const& key) const {
515  static_assert(std::is_const<T>::value,
516  "Due to implementation constraints, pointers to non-const are not supported.");
517  // Avoid actually getting and casting an object, if at all possible
518  if (!contains(key.getId())) {
519  return false;
520  }
521 
522  auto foo = unsafeLookup(key.getId());
523  try {
524  auto pointer = boost::get<std::shared_ptr<Storable const> const&>(foo);
525  if (pointer == nullptr) { // Can't confirm type with dynamic_cast
526  return true;
527  }
528  std::shared_ptr<T> typedPointer = std::dynamic_pointer_cast<T>(pointer);
529  // shared_ptr can be empty without being null. dynamic_pointer_cast
530  // only promises result of failed cast is empty, so test for that
531  return typedPointer.use_count() > 0;
532  } catch (boost::bad_get const&) {
533  return false;
534  }
535  }
536 
537  // Type alias to properly handle Visitor output
538  // Assume that each operator() has the same return type; variant will enforce it
540  template <class Visitor>
541  using _VisitorResult = std::result_of_t<Visitor && (K&&, bool&)>;
543 
544  // No return value, const GenericMap
545  template <class Visitor, typename std::enable_if_t<std::is_void<_VisitorResult<Visitor>>::value, int> = 0>
546  void _apply(Visitor&& visitor) const {
547  for (K const& key : keys()) {
548  boost::variant<K> varKey = key;
549  boost::apply_visitor(visitor, varKey, unsafeLookup(key));
550  }
551  }
552 
553  // Return value, const GenericMap
554  template <class Visitor,
555  typename std::enable_if_t<!std::is_void<_VisitorResult<Visitor>>::value, int> = 0>
556  auto _apply(Visitor&& visitor) const {
558  results.reserve(size());
559 
560  for (K const& key : keys()) {
561  boost::variant<K> varKey = key;
562  results.emplace_back(boost::apply_visitor(visitor, varKey, unsafeLookup(key)));
563  }
564  return results;
565  }
566 
567  // No return value, non-const GenericMap
568  template <class Visitor, typename std::enable_if_t<std::is_void<_VisitorResult<Visitor>>::value, int> = 0>
569  void _apply(Visitor&& visitor) {
570  for (K const& key : keys()) {
571  boost::variant<K> varKey = key;
572  // Boost gets confused if we pass it a temporary variant
574  boost::apply_visitor(visitor, varKey, ref);
575  }
576  }
577 
578  // Return value, non-const GenericMap
579  template <class Visitor,
580  typename std::enable_if_t<!std::is_void<_VisitorResult<Visitor>>::value, int> = 0>
581  auto _apply(Visitor&& visitor) {
583  results.reserve(size());
584 
585  for (K const& key : keys()) {
586  boost::variant<K> varKey = key;
587  // Boost gets confused if we pass it a temporary variant
589  results.emplace_back(boost::apply_visitor(visitor, varKey, ref));
590  }
591  return results;
592  }
593 };
594 
605 template <typename K>
606 class MutableGenericMap : public GenericMap<K> {
607 protected:
608  using typename GenericMap<K>::StorableType;
609 
610 public:
611  virtual ~MutableGenericMap() noexcept = default;
612 
618  virtual void clear() noexcept = 0;
619 
638  template <typename T>
639  bool insert(Key<K, T> const& key, T const& value) {
640  if (this->contains(key.getId())) {
641  return false;
642  }
643 
644  return unsafeInsert(key.getId(), StorableType(value));
645  }
646 
662  template <typename T>
663  std::pair<Key<K, T>, bool> insert(K const& key, T const& value) {
664  auto strongKey = makeKey<T>(key);
665  // Construct return value in advance, so that exception from copying/moving Key is atomic
666  auto result = make_pair(strongKey, false);
667  result.second = insert(strongKey, value);
668  return result;
669  }
670 
684  template <typename T>
685  bool erase(Key<K, T> const& key) {
686  if (this->contains(key)) {
687  return unsafeErase(key.getId());
688  } else {
689  return false;
690  }
691  }
692 
693 protected:
706  virtual bool unsafeInsert(K key, StorableType&& value) = 0;
707 
717  virtual bool unsafeErase(K key) = 0;
718 };
719 
720 } // namespace typehandling
721 } // namespace afw
722 } // namespace lsst
723 
724 #endif
py::object result
Definition: _schema.cc:430
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
ItemVariant const * other
Definition: Schema.cc:56
Key< U > key
Definition: Schema.cc:281
Interface for a heterogeneous map.
Definition: GenericMap.h:78
boost::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:358
size_type count(Key< K, T > const &key) const
Return the number of elements mapped to the specified key.
Definition: GenericMap.h:156
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:114
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:193
decltype(_typeToRef(std::declval< StorableType >())) ValueReference
A type-agnostic reference to the value stored inside the map.
Definition: GenericMap.h:368
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:120
auto apply(Visitor &&visitor)
Apply a modifying operation to each key-value pair in the map.
Definition: GenericMap.h:328
decltype(_typeToConstRef(std::declval< StorableType >())) ConstValueReference
A type-agnostic reference to the value stored inside the map.
Definition: GenericMap.h:367
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:108
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.
auto apply(Visitor &&visitor) const
Apply an operation to each key-value pair in the map.
Definition: GenericMap.h:306
ValueReference unsafeLookup(K key)
Return a reference to the mapped value of the element with key equal to key.
Definition: GenericMap.h:389
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:242
Key for type-safe lookup in a GenericMap.
Definition: Key.h:52
Interface for a GenericMap that allows element addition and removal.
Definition: GenericMap.h:606
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:639
bool erase(Key< K, T > const &key)
Remove the mapping for a key from this map, if it exists.
Definition: GenericMap.h:685
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:663
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)
A base class for image defects.
STL namespace.
T ref(T... args)
T reserve(T... args)
T str(T... args)