IMP logo
IMP Reference Guide  develop.63b38c487d,2024/12/25
The Integrative Modeling Platform
Key.h
Go to the documentation of this file.
1 /**
2  * \file IMP/Key.h \brief Keys to cache lookup of attribute strings.
3  *
4  * Copyright 2007-2022 IMP Inventors. All rights reserved.
5  *
6  */
7 
8 #ifndef IMPKERNEL_KEY_H
9 #define IMPKERNEL_KEY_H
10 
11 #include "utility.h"
12 #include "internal/key_helpers.h"
13 #include <IMP/check_macros.h>
14 #include <IMP/comparison_macros.h>
15 #include <IMP/hash_macros.h>
16 #include <IMP/log_macros.h>
17 #include <IMP/thread_macros.h>
18 #include <IMP/Value.h>
19 #include <cereal/access.hpp>
20 #include <vector>
21 
22 IMPKERNEL_BEGIN_NAMESPACE
23 
24 //! A base class for Keys
25 /** This class does internal caching of the strings to accelerate the
26  name lookup. It is better to create a Key and reuse it
27  rather than recreate it many times from strings.
28 
29  If you use this with a new type, you must add a new definition of
30  attribute_table_index. Yes, this is an evil hack, but I couldn't
31  get linking to work with static members of the template class.
32 
33  The keys in \imp maintain a cached mapping between strings and indexes.
34  This mapping is global--that is all \imp Models and Particles in the
35  same program use the same mapping for each type of key. The type of
36  the key is determined by an integer which should be unique for
37  each type. If the integer is not unique, everything works, just
38  more memory is wasted and types are interconvertible.
39 
40  Keys used for storing attributes in particles should never be statically
41  initialized. While this is annoying, statically initializing them is bad,
42  as unused attribute keys can result in wasted memory in each particle.
43  */
44 template <unsigned int ID>
45 class Key : public Value {
46  private:
47 
48  //! returns a static structure with mapping between
49  //! key int identifiers and strings
50  static internal::KeyData& get_key_data() {
51 #ifndef IMPKERNEL_INTERNAL_OLD_COMPILER
52  static internal::KeyData static_key_data_(ID);
53  return static_key_data_;
54 #else
55  return IMP::internal::get_key_data(ID);
56 #endif
57  }
58 
59  private:
60  int str_;
61 
62  friend class cereal::access;
63 
64  template<class Archive> void serialize(Archive &ar) {
65  // Serialize Keys by string, not the internal index, which could change
66  if (std::is_base_of<cereal::detail::OutputArchiveBase, Archive>::value) {
67  std::string name = get_string();
68  ar(name);
69  } else {
70  std::string name;
71  ar(name);
72  str_ = find_or_add_index(name);
73  }
74  }
75 
76  static const internal::KeyData::Map& get_map() {
77  return get_key_data().get_map();
78  }
79  static const internal::KeyData::RMap& get_rmap() {
80  IMP::internal::KeyData const& kd=get_key_data();
81  IMP::internal::KeyData::RMap const& ret=kd.get_rmap();
82  return ret;
83  }
84 
85  //! returns the index of sc, adds it if it's not there
86  static unsigned int find_or_add_index(std::string const& sc) {
87  IMP_USAGE_CHECK(!sc.empty(), "Can't create a key with an empty name");
88  unsigned int val;
89  IMP_OMP_PRAGMA(critical(imp_key)) {
90  if (get_map().find(sc) == get_map().end()) {
91  val = get_key_data().add_key(sc);
92  } else {
93  val = get_map().find(sc)->second;
94  }
95  }
96  return val;
97  }
98 
99 
100  static unsigned int find_index(std::string const& sc) {
101  IMP_USAGE_CHECK(!sc.empty(), "Can't create a key with an empty name");
102  unsigned int val;
103  IMP_OMP_PRAGMA(critical(imp_key)) {
104  IMP_USAGE_CHECK( get_key_exists(sc), "Key<" << ID << ">::find_index():"
105  << " You must explicitly create the type first: "
106  << sc);
107  val = get_map().find(sc)->second;
108  }
109  return val;
110  }
111 
112  private:
113  bool is_default() const;
114 
115  public:
116 #if !defined(IMP_DOXYGEN) && !defined(SWIG)
117  static unsigned int get_ID() { return ID; }
118 
119  static const std::string get_string(int i) {
120  std::string val;
121  IMP_OMP_PRAGMA(critical(imp_key)) {
122  if (static_cast<unsigned int>(i) < get_rmap().size()) {
123  val = get_rmap()[i];
124  }
125  }
126  if (val.empty()) {
127  IMP_FAILURE("Corrupted Key Table asking for key "
128  << i << " with a table of size " << get_rmap().size());
129  }
130  return val;
131  }
132 
133 #endif
134  //! make a default key in a well-defined null state
135  Key() : str_(-1) {}
136 
137  //! Generate a key object from the given string
138  /**
139  Generate a key object from the given string.
140 
141  @param c key string representation
142  @param is_implicit_add_permitted If true, a key for c can be created even if it hasn't
143  been created earlier. If false, than it is assumed that a key for c
144  has already been instantiated by e.g., a previous
145  call to Key(c, true) or using Key::add_key().
146  Formally, it is assumed that get_has_key(c) is true.
147 
148  @note This operation can be expensive, so please cache the result.
149  */
150  explicit Key(std::string const& c, bool is_implicit_add_permitted=true)
151  : str_(is_implicit_add_permitted ? find_or_add_index(c) : find_index(c))
152  {}
153 
154 #if !defined(IMP_DOXYGEN)
155  //! this is a fast and lean constructor that should be used
156  //! whenever performance is of the essence
157  explicit Key(unsigned int i) : str_(i) {
158  IMP_INTERNAL_CHECK(str_ >= 0, "Invalid initializer " << i);
159  // cannot check here as we need a past end iterator
160  }
161 #endif
162 
163  static unsigned int add_key(std::string sc) {
164  IMP_USAGE_CHECK(!sc.empty(), "Can't create a key with an empty name");
165  unsigned int val;
166  IMP_OMP_PRAGMA(critical(imp_key))
167  IMP_LOG_PROGRESS("Key::add_key " << sc << " ID " << ID << std::endl);
168  val = get_key_data().add_key(sc);
169  return val;
170  }
171 
172  //! Return true if there already is a key with that string
173  static bool get_key_exists(std::string sc) {
174  bool val;
175  IMP_OMP_PRAGMA(critical(imp_key))
176  val = get_map().find(sc) != get_map().end();
177  return val;
178  }
179 
180  //! Turn a key into a pretty string
181  const std::string get_string() const {
182  if (is_default()) return std::string("nullptr");
183  std::string val;
184  val = get_string(str_);
185  return val;
186  }
187 
188  IMP_COMPARISONS_1(Key, str_);
189 
190  IMP_HASHABLE_INLINE(Key, return str_;)
191 
192  IMP_SHOWABLE_INLINE(Key, out << "\"" << get_string() << "\"";);
193 
194  //! Make new_name an alias for old_key
195  /** Afterwards
196  \code
197  Key<ID>(old_key.get_string()) == Key<ID>(new_name)
198  \endcode
199  */
200  static Key<ID> add_alias(Key<ID> old_key,
201  std::string new_name) {
202  IMP_INTERNAL_CHECK( get_map().find(new_name) == get_map().end(),
203  "The name is already taken with an existing key or alias");
204  get_key_data().add_alias(new_name, old_key.get_index());
205  return Key<ID>(new_name.c_str());
206  }
207 
208  static unsigned int get_number_of_keys() {
209  return get_rmap().size();
210  }
211 
212 #ifndef DOXYGEN
213  unsigned int get_index() const {
214  IMP_INTERNAL_CHECK(!is_default(),
215  "Cannot get index on defaultly constructed Key");
216  return str_;
217  }
218 #endif
219 
220  //! Show all the keys of this type
221  static void show_all(std::ostream& out);
222 
223  //! Get a list of all of the keys of this type
224  /**
225  This can be used to check for typos and similar keys.
226  */
227  static Vector<std::string> get_all_strings();
228 
229  //! Get the total number of keys of this type
230  /**
231  This is mostly for debugging to make sure that there are no extra
232  keys created.
233  */
234  static unsigned int get_number_unique() { return get_rmap().size(); }
235 
236 #ifndef SWIG
237  /** \todo These should be protected, I'll try to work how
238  */
240  ++str_;
241  return *this;
242  }
243  Key& operator--() {
244  --str_;
245  return *this;
246  }
247  Key operator+(int o) const {
248  Key c = *this;
249  c.str_ += o;
250  return c;
251  }
252 #endif
253 };
254 
255 #ifndef IMP_DOXYGEN
256 
257 
258 template <unsigned int ID>
259 inline std::ostream& operator<<(std::ostream& out, Key<ID> k) {
260  k.show(out);
261  return out;
262 }
263 
264 template <unsigned int ID>
265 inline bool Key<ID>::is_default() const {
266  return str_ == -1;
267 }
268 
269 template <unsigned int ID>
270  inline void Key<ID>::show_all(std::ostream& out) {
271  IMP_OMP_PRAGMA(critical(imp_key))
272  get_key_data().show(out);
273 }
274 
275 template <unsigned int ID>
276 Vector<std::string> Key<ID>::get_all_strings() {
277  Vector<std::string> str;
278  IMP_OMP_PRAGMA(critical(imp_key))
279  for (internal::KeyData::Map::const_iterator it = get_map().begin();
280  it != get_map().end(); ++it) {
281  str.push_back(it->first);
282  }
283  return str;
284 }
285 #endif
286 
287 IMPKERNEL_END_NAMESPACE
288 
289 #endif /* IMPKERNEL_KEY_H */
290 
Key & operator++()
Definition: Key.h:239
#define IMP_SHOWABLE_INLINE(Name, how_to_show)
Declare the methods needed by an object that can be printed.
Helper macros for implementing comparisons of IMP objects.
#define IMP_FAILURE(message)
A runtime failure for IMP.
Definition: check_macros.h:72
#define IMP_HASHABLE_INLINE(name, hashret)
Definition: hash_macros.h:18
Key()
make a default key in a well-defined null state
Definition: Key.h:135
#define IMP_LOG_PROGRESS(expr)
Definition: log_macros.h:94
const std::string get_string() const
Turn a key into a pretty string.
Definition: Key.h:181
#define IMP_INTERNAL_CHECK(expr, message)
An assertion to check for internal errors in IMP. An IMP::ErrorException will be thrown.
Definition: check_macros.h:139
Base class for a simple primitive-like type.
Definition: Value.h:23
static unsigned int get_number_unique()
Get the total number of keys of this type.
Definition: Key.h:234
Ints get_index(const ParticlesTemp &particles, const Subset &subset, const Subsets &excluded)
Helper macros for implementing hashable classes.
Logging and error reporting support.
A base class for Keys.
Definition: Key.h:45
Various general useful functions for IMP.
std::ostream & show(Hierarchy h, std::ostream &out=std::cout)
Print the hierarchy using a given decorator to display each node.
Helper macros for throwing and handling exceptions.
Base class for a simple primitive-like type.
#define IMP_USAGE_CHECK(expr, message)
A runtime test for incorrect usage of a class or method.
Definition: check_macros.h:168
static Key< ID > add_alias(Key< ID > old_key, std::string new_name)
Make new_name an alias for old_key.
Definition: Key.h:200
#define IMP_COMPARISONS_1(Name, field)
Implement comparison in a class using field as the variable to compare.
Key(std::string const &c, bool is_implicit_add_permitted=true)
Generate a key object from the given string.
Definition: Key.h:150
Control for OpenMP.
#define IMP_OMP_PRAGMA(x)
Definition: thread_macros.h:35