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