IMP  2.0.0
The Integrative Modeling Platform
kernel/Key.h
Go to the documentation of this file.
1 /**
2  * \file IMP/kernel/Key.h \brief Keys to cache lookup of attribute strings.
3  *
4  * Copyright 2007-2013 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/base/check_macros.h>
15 #include <IMP/base/hash_macros.h>
16 #include <IMP/base/thread_macros.h>
17 #include <IMP/base/Value.h>
18 #include <vector>
19 
20 IMPKERNEL_BEGIN_NAMESPACE
21 
22 //! A base class for Keys
23 /** This class does internal caching of the strings to accelerate the
24  name lookup. It is better to create a Key and reuse it
25  rather than recreate it many times from strings.
26 
27  If you use this with a new type, you must add a new definition of
28  attribute_table_index. Yes, this is an evil hack, but I couldn't
29  get linking to work with static members of the template class.
30 
31  The keys in \imp maintain a cached mapping between strings and indexes.
32  This mapping is global--that is all \imp Models and Particles in the
33  same program use the same mapping for each type of key. The type of
34  the key is determined by an integer which should be unique for
35  each type. If the integer is not unique, everything works, just
36  more memory is wasted and types are interconvertible.
37 
38  Keys used for storing attributes in particles should never be statically
39  initialized. While this is annoying, statically initializing them is bad,
40  as unused attribute keys can result in wasted memory in each particle.
41 
42  If LazyAdd is true, keys created with a new string will be added,
43  otherwise this is an error.
44  */
45 template <unsigned int ID, bool LazyAdd>
46 class Key: public base::Value
47 {
48  int str_;
49 
50  static const internal::KeyData::Map& get_map()
51  {
52  return IMP::kernel::internal::get_key_data(ID).get_map();
53  }
54  static const internal::KeyData::RMap& get_rmap() {
55  return IMP::kernel::internal::get_key_data(ID).get_rmap();
56  }
57 
58 
59  static unsigned int find_index(std::string sc) {
60  IMP_USAGE_CHECK(!sc.empty(),
61  "Can't create a key with an empty name");
62  unsigned int val;
63 IMP_OMP_PRAGMA(critical(imp_key))
64  {
65  if (get_map().find(sc) == get_map().end()) {
66  IMP_INTERNAL_CHECK(LazyAdd, "You must explicitly create the type"
67  << " first: " << sc);
68  val= IMP::kernel::internal::get_key_data(ID).add_key(sc);
69  } else {
70  val= get_map().find(sc)->second;
71  }
72  }
73  return val;
74  }
75 private:
76  bool is_default() const;
77 public:
78 #if !defined(IMP_DOXYGEN) && !defined(SWIG)
79  static unsigned int get_ID() {
80  return ID;
81  }
82 
83  static const std::string get_string(int i)
84  {
85  std::string val;
86 IMP_OMP_PRAGMA(critical(imp_key))
87  {
88  if (static_cast<unsigned int>(i)
89  < get_rmap().size()) {
90  val= get_rmap()[i];
91  }
92  }
93  if (val.empty()) {
94  IMP_FAILURE("Corrupted Key Table asking for key " << i
95  << " with a table of size " << get_rmap().size());
96  }
97  return val;
98  }
99 
100 #endif
101  //! make a default key in a well-defined null state
102  Key(): str_(-1) {}
103 
104  //! Generate a key from the given string
105  /** This operation can be expensive, so please cache the result.*/
106  explicit Key(std::string c): str_(find_index(c)) {
107  }
108 
109 
110 #if !defined(IMP_DOXYGEN)
111  explicit Key(unsigned int i): str_(i) {
112  IMP_INTERNAL_CHECK(str_ >= 0, "Invalid initializer " << i);
113  // cannot check here as we need a past end iterator
114  }
115 #endif
116 
117  static unsigned int add_key(std::string sc) {
118  IMP_USAGE_CHECK(!sc.empty(),
119  "Can't create a key with an empty name");
120  unsigned int val;
121 IMP_OMP_PRAGMA(critical(imp_key))
122  val= IMP::kernel::internal::get_key_data(ID).add_key(sc);
123  return val;
124  }
125 
126  //! Return true if there already is a key with that string
127  static bool get_key_exists(std::string sc) {
128  bool val;
129 IMP_OMP_PRAGMA(critical(imp_key))
130  val= get_map().find(sc) != get_map().end();
131  return val;
132  }
133 
134  //! Turn a key into a pretty string
135  const std::string get_string() const {
136  if (is_default()) return std::string("nullptr");
137  std::string val;
138  val= get_string(str_);
139  return val;
140  }
141 
142  IMP_COMPARISONS_1(Key, str_);
143 
144  IMP_HASHABLE_INLINE(Key, return str_;)
145 
146  IMP_SHOWABLE_INLINE(Key, out << "\"" << get_string() << "\"";);
147 
148  //! Make new_name an alias for old_key
149  /** Afterwards
150  \code
151  Key<ID>(old_key.get_string()) == Key<ID>(new_name)
152  \endcode
153  */
154  static Key<ID, LazyAdd> add_alias(Key<ID, LazyAdd> old_key,
155  std::string new_name) {
156  IMP_INTERNAL_CHECK(get_map().find(new_name) == get_map().end(),
157  "The name is already taken with an existing key or alias");
158  IMP::kernel::internal::get_key_data(ID).add_alias(new_name,
159  old_key.get_index());
160  return Key<ID, LazyAdd>(new_name.c_str());
161  }
162 
163 #ifndef DOXYGEN
164  unsigned int get_index() const {
165  IMP_INTERNAL_CHECK(!is_default(),
166  "Cannot get index on defaultly constructed Key");
167  return str_;
168  }
169 #endif
170 
171  //! Show all the keys of this type
172  static void show_all(std::ostream &out);
173 
174  //! Get a list of all of the keys of this type
175  /**
176  This can be used to check for typos and similar keys.
177  */
178  static base::Vector<std::string> get_all_strings();
179 
180  //! Get the total number of keys of this type
181  /**
182  This is mostly for debugging to make sure that there are no extra
183  keys created.
184  */
185  static unsigned int get_number_unique() {
186  return get_rmap().size();
187  }
188 
189 #ifndef SWIG
190  /** \todo These should be protected, I'll try to work how
191  */
193  ++str_;
194  return *this;
195  }
196  Key& operator--() {
197  --str_;
198  return *this;
199  }
200  Key operator+(int o) const {
201  Key c=*this;
202  c.str_+= o;
203  return c;
204  }
205 #endif
206 };
207 
208 #ifndef IMP_DOXYGEN
209 
210 template <unsigned int ID, bool LA>
211 inline std::ostream &operator<<(std::ostream &out, Key<ID, LA> k) {
212  k.show(out);
213  return out;
214 }
215 
216 template <unsigned int ID, bool LA>
217 inline bool Key<ID, LA>::is_default() const
218 {
219  return str_==-1;
220 }
221 
222 
223 template <unsigned int ID, bool LA>
224 inline void Key<ID, LA>::show_all(std::ostream &out)
225 {
226 IMP_OMP_PRAGMA(critical(imp_key))
227  internal::get_key_data(ID).show(out);
228 }
229 
230 template <unsigned int ID, bool LA>
231 base::Vector<std::string> Key<ID, LA>::get_all_strings()
232 {
233  base::Vector<std::string> str;
234 IMP_OMP_PRAGMA(critical(imp_key))
235  for (internal::KeyData::Map::const_iterator it= get_map().begin();
236  it != get_map().end(); ++it) {
237  str.push_back(it->first);
238  }
239  return str;
240 }
241 #endif
242 
243 IMPKERNEL_END_NAMESPACE
244 
245 #endif /* IMPKERNEL_KEY_H */