IMP logo
IMP Reference Guide  develop.4eee3cf66f,2026/08/02
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-2026 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  // An empty string corresponds to a default-initialized Key (it is not
67  // allowed to create a real Key with an empty name).
68  if (std::is_base_of<cereal::detail::OutputArchiveBase, Archive>::value) {
69  std::string name;
70  if (!is_default()) {
71  name = get_string();
72  }
73  ar(name);
74  } else {
75  std::string name;
76  ar(name);
77  if (name.empty()) {
78  str_ = -1;
79  } else {
80  str_ = find_or_add_index(name);
81  }
82  }
83  }
84 
85  static const internal::KeyData::Map& get_map() {
86  return get_key_data().get_map();
87  }
88  static const internal::KeyData::RMap& get_rmap() {
89  IMP::internal::KeyData const& kd=get_key_data();
90  IMP::internal::KeyData::RMap const& ret=kd.get_rmap();
91  return ret;
92  }
93 
94  //! returns the index of sc, adds it if it's not there
95  static unsigned int find_or_add_index(std::string const& sc) {
96  IMP_USAGE_CHECK(!sc.empty(), "Can't create a key with an empty name");
97  unsigned int val;
98  IMP_OMP_PRAGMA(critical(imp_key)) {
99  if (get_map().find(sc) == get_map().end()) {
100  val = get_key_data().add_key(sc);
101  } else {
102  val = get_map().find(sc)->second;
103  }
104  }
105  return val;
106  }
107 
108 
109  static unsigned int find_index(std::string const& sc) {
110  IMP_USAGE_CHECK(!sc.empty(), "Can't create a key with an empty name");
111  unsigned int val;
112  IMP_OMP_PRAGMA(critical(imp_key)) {
113  IMP_USAGE_CHECK( get_key_exists(sc), "Key<" << ID << ">::find_index():"
114  << " You must explicitly create the type first: "
115  << sc);
116  val = get_map().find(sc)->second;
117  }
118  return val;
119  }
120 
121  private:
122  bool is_default() const;
123 
124  public:
125 #if !defined(IMP_DOXYGEN) && !defined(SWIG)
126  static unsigned int get_ID() { return ID; }
127 
128  static const std::string get_string(int i) {
129  std::string val;
130  IMP_OMP_PRAGMA(critical(imp_key)) {
131  if (static_cast<unsigned int>(i) < get_rmap().size()) {
132  val = get_rmap()[i];
133  }
134  }
135  if (val.empty()) {
136  IMP_FAILURE("Corrupted Key Table asking for key "
137  << i << " with a table of size " << get_rmap().size());
138  }
139  return val;
140  }
141 
142 #endif
143  //! make a default key in a well-defined null state
144  Key() : str_(-1) {}
145 
146  //! Generate a key object from the given string
147  /**
148  Generate a key object from the given string.
149 
150  @param c key string representation
151  @param is_implicit_add_permitted If true, a key for c can be created even if it hasn't
152  been created earlier. If false, than it is assumed that a key for c
153  has already been instantiated by e.g., a previous
154  call to Key(c, true) or using Key::add_key().
155  Formally, it is assumed that get_has_key(c) is true.
156 
157  @note This operation can be expensive, so please cache the result.
158  */
159  explicit Key(std::string const& c, bool is_implicit_add_permitted=true)
160  : str_(is_implicit_add_permitted ? find_or_add_index(c) : find_index(c))
161  {}
162 
163 #if !defined(IMP_DOXYGEN)
164  //! this is a fast and lean constructor that should be used
165  //! whenever performance is of the essence
166  explicit Key(unsigned int i) : str_(i) {
167  IMP_INTERNAL_CHECK(str_ >= 0, "Invalid initializer " << i);
168  // cannot check here as we need a past end iterator
169  }
170 #endif
171 
172  static unsigned int add_key(std::string sc) {
173  IMP_USAGE_CHECK(!sc.empty(), "Can't create a key with an empty name");
174  unsigned int val;
175  IMP_OMP_PRAGMA(critical(imp_key))
176  IMP_LOG_PROGRESS("Key::add_key " << sc << " ID " << ID << std::endl);
177  val = get_key_data().add_key(sc);
178  return val;
179  }
180 
181  //! Return true if there already is a key with that string
182  static bool get_key_exists(std::string sc) {
183  bool val;
184  IMP_OMP_PRAGMA(critical(imp_key))
185  val = get_map().find(sc) != get_map().end();
186  return val;
187  }
188 
189  //! Turn a key into a pretty string
190  const std::string get_string() const {
191  if (is_default()) return std::string("nullptr");
192  std::string val;
193  val = get_string(str_);
194  return val;
195  }
196 
197  IMP_COMPARISONS_1(Key, str_);
198 
199  IMP_HASHABLE_INLINE(Key, return str_;)
200 
201  IMP_SHOWABLE_INLINE(Key, out << "\"" << get_string() << "\"";);
202 
203  //! Make new_name an alias for old_key
204  /** Afterwards
205  \code
206  Key<ID>(old_key.get_string()) == Key<ID>(new_name)
207  \endcode
208  */
209  static Key<ID> add_alias(Key<ID> old_key,
210  std::string new_name) {
211  IMP_INTERNAL_CHECK( get_map().find(new_name) == get_map().end(),
212  "The name is already taken with an existing key or alias");
213  get_key_data().add_alias(new_name, old_key.get_index());
214  return Key<ID>(new_name.c_str());
215  }
216 
217  static unsigned int get_number_of_keys() {
218  return get_rmap().size();
219  }
220 
221 #ifndef DOXYGEN
222  unsigned int get_index() const {
223  IMP_INTERNAL_CHECK(!is_default(),
224  "Cannot get index on defaultly constructed Key");
225  return str_;
226  }
227 #endif
228 
229  //! Show all the keys of this type
230  static void show_all(std::ostream& out);
231 
232  //! Get a list of all of the keys of this type
233  /**
234  This can be used to check for typos and similar keys.
235  */
236  static Vector<std::string> get_all_strings();
237 
238  //! Get the total number of keys of this type
239  /**
240  This is mostly for debugging to make sure that there are no extra
241  keys created.
242  */
243  static unsigned int get_number_unique() { return get_rmap().size(); }
244 
245 #ifndef SWIG
246  /** \todo These should be protected, I'll try to work how
247  */
249  ++str_;
250  return *this;
251  }
252  Key& operator--() {
253  --str_;
254  return *this;
255  }
256  Key operator+(int o) const {
257  Key c = *this;
258  c.str_ += o;
259  return c;
260  }
261 #endif
262 };
263 
264 #ifndef IMP_DOXYGEN
265 
266 
267 template <unsigned int ID>
268 inline std::ostream& operator<<(std::ostream& out, Key<ID> k) {
269  k.show(out);
270  return out;
271 }
272 
273 template <unsigned int ID>
274 inline bool Key<ID>::is_default() const {
275  return str_ == -1;
276 }
277 
278 template <unsigned int ID>
279  inline void Key<ID>::show_all(std::ostream& out) {
280  IMP_OMP_PRAGMA(critical(imp_key))
281  get_key_data().show(out);
282 }
283 
284 template <unsigned int ID>
285 Vector<std::string> Key<ID>::get_all_strings() {
286  Vector<std::string> str;
287  IMP_OMP_PRAGMA(critical(imp_key))
288  for (internal::KeyData::Map::const_iterator it = get_map().begin();
289  it != get_map().end(); ++it) {
290  str.push_back(it->first);
291  }
292  return str;
293 }
294 #endif
295 
296 IMPKERNEL_END_NAMESPACE
297 
298 #endif /* IMPKERNEL_KEY_H */
299 
Key & operator++()
Definition: Key.h:248
#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:144
#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:190
#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:243
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:209
#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:159
Control for OpenMP.
#define IMP_OMP_PRAGMA(x)
Definition: thread_macros.h:35