IMP logo
IMP Reference Guide  develop.182d9373c4,2021/04/12
The Integrative Modeling Platform
container_macros.h
Go to the documentation of this file.
1 /**
2  * \file IMP/container_macros.h
3  * \brief Macros to define containers of objects
4  *
5  * Copyright 2007-2021 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef IMPKERNEL_CONTAINER_MACROS_H
10 #define IMPKERNEL_CONTAINER_MACROS_H
11 
12 #include <IMP/kernel_config.h>
13 #include <IMP/check_macros.h>
14 #include <IMP/internal/Vector.h>
15 #include <IMP/SetCheckState.h>
16 #include <IMP/log_macros.h>
17 #include <IMP/doxygen_macros.h>
18 #include <algorithm>
19 
20 // Swig doesn't do protection right with protected members
21 #ifdef IMP_SWIG_WRAPPER
22 #define IMP_PROTECTION(protection) public:
23 #else
24 #define IMP_PROTECTION(protection) protection:
25 #endif
26 
27 #ifndef SWIG
28 /** Internal use only. */
29 #define IMP_EXPOSE_ITERATORS(ContainerType, container_name, Ucname, Ucnames, \
30  lcname, lcnames) \
31  IMP_SWITCH_DOXYGEN( \
32  class Ucname##Iterator; \
33  class Ucname##ConstIterator, \
34  typedef ContainerType::iterator Ucname##Iterator; \
35  typedef ContainerType::const_iterator Ucname##ConstIterator); \
36  Ucname##Iterator lcnames##_begin() { return container_name.begin(); } \
37  Ucname##Iterator lcnames##_end() { return container_name.end(); } \
38  Ucname##ConstIterator lcnames##_begin() const { \
39  return container_name.begin(); \
40  } \
41  Ucname##ConstIterator lcnames##_end() const { return container_name.end(); }
42 
43 #else
44 #define IMP_EXPOSE_ITERATORS(ContainerType, container_name, Ucname, Ucnames, \
45  lcname, lcnames)
46 #endif // SWIG
47 
48 #ifdef GCC_VISIBILITY
49 #define IMP_FORCE_EXPORT(x) __attribute__((visibility("default"))) x
50 #else
51 #define IMP_FORCE_EXPORT(x) x
52 #endif
53 
54 /** \brief A macro to provide a uniform interface for storing lists of
55  objects.
56 
57  This macro is designed to be used in the body of an object to add
58  a set of methods for manipulating a list of contained objects. It adds
59  methods
60  - get_foo
61  - set_foo
62  - set_foos
63  - foos_begin, foos_end
64  - remove_foo
65  - remove_foos
66  etc. where foo is the lcname provided.
67 
68  \param[in] protection The level of protection for the container
69  (public, private).
70  \param[in] Ucname The name of the type of container in uppercase.
71  \param[in] lcname The name of the type of container in lower case.
72  \param[in] Data The type of the data to store.
73  \param[in] PluralData The plural of the data name. This should be a
74  container type.
75 
76  An accompanying IMP_LIST_IMPL must go in a \c .cpp file.
77 
78  \note This macro should be given an appropriate name and wrapped in a
79  doxygen comment block such as by starting with a C++ comment like
80  \verbatim
81  @name short description
82  longer description
83  @{
84  \endverbatim
85  and ending with one like
86  \verbatim
87  @}
88  \endverbatim
89 */
90 #define IMP_LIST(protection, Ucname, lcname, Data, PluralData) \
91  IMP_LIST_ACTION(protection, Ucname, Ucname##s, lcname, lcname##s, Data, \
92  PluralData, , , )
93 
94 #if defined(SWIG) || defined(IMP_DOXYGEN)
95 
96 #define IMP_LIST_ACTION(protection, Ucname, Ucnames, lcname, lcnames, Data, \
97  PluralData, OnAdd, OnChanged, OnRemoved) \
98  public: \
99  void remove_##lcname(Data d); \
100  void remove_##lcnames(const PluralData& d); \
101  void set_##lcnames(const PluralData& ps); \
102  void set_##lcnames##_order(const PluralData& objs); \
103  unsigned int add_##lcname(Data obj); \
104  void add_##lcnames(const PluralData& objs); \
105  void clear_##lcnames(); \
106  unsigned int get_number_of_##lcnames() const; \
107  bool get_has_##lcnames(); \
108  Data get_##lcname(unsigned int i) const; \
109  PluralData get_##lcnames() const; \
110  void reserve_##lcnames(unsigned int sz)
111 
112 #else
113 /** A version of IMP_LIST() for types where the spelling of the plural is
114  irregular (eg geometry-> geometries) and where actions can be taken
115  upon addition and removal:
116  \param[in] protection The level of protection for the container
117  (public, private).
118  \param[in] Ucname The name of the type of container in uppercase.
119  \param[in] lcname The name of the type of container in lower case.
120  \param[in] Data The type of the data to store.
121  \param[in] PluralData The plural of the data name. This should be a
122  container type.
123  \param[in] OnAdd Code to modify the passed in object. The object is obj
124  and its index index.
125  \param[in] OnChanged Code to get executed when the container changes.
126  \param[in] OnRemoved Code to get executed when the an object is removed.
127 */
128 #define IMP_LIST_ACTION(protection, Ucname, Ucnames, lcname, lcnames, Data, \
129  PluralData, OnAdd, OnChanged, OnRemoved) \
130  IMP_PROTECTION(protection) \
131  /** \brief Remove any occurences of d from the container. */ \
132  void remove_##lcname(Data d) { \
133  IMP_OBJECT_LOG; \
134  bool found = false; \
135  for (Ucname##Iterator it = lcnames##_begin(); it != lcnames##_end(); \
136  ++it) { \
137  if (*it == d) { \
138  lcname##_handle_remove(*it); \
139  found = true; \
140  lcname##_vector_.erase(it); \
141  break; \
142  } \
143  } \
144  IMP_UNUSED(found); \
145  IMP_USAGE_CHECK(found, d << " not found in container: " \
146  << get_as<PluralData>(lcname##_vector_)); \
147  lcname##_handle_change(); \
148  } \
149  /** \brief Remove any occurrences for which f is true */ \
150  template <class F> \
151  void remove_##lcnames##_if(const F& f) { \
152  IMP_OBJECT_LOG; \
153  for (Ucname##Iterator it = lcnames##_begin(); it != lcnames##_end(); \
154  ++it) { \
155  if (f(*it)) lcname##_handle_remove(*it); \
156  } \
157  lcname##_vector_.erase( \
158  std::remove_if(lcname##_vector_.begin(), lcname##_vector_.end(), f), \
159  lcname##_vector_.end()); \
160  lcname##_handle_change(); \
161  } \
162  /** \brief Remove any occurences of each item in d. */ \
163  template <class List> \
164  void remove_##lcnames(List d) { \
165  IMP_OBJECT_LOG; \
166  Vector<Data> ds(d.begin(), d.end()); \
167  std::sort(ds.begin(), ds.end()); \
168  for (unsigned int i = 0; i < ds.size(); ++i) { \
169  lcname##_handle_remove(ds[i]); \
170  } \
171  lcname##_vector_.erase( \
172  std::remove_if(lcname##_vector_.begin(), lcname##_vector_.end(), \
173  ::IMP::internal::list_contains(ds)), \
174  lcname##_vector_.end()); \
175  } \
176  /** \brief Set the contents of the container to ps removing all its current
177  contents.
178  */ \
179  template <class List> \
180  void set_##lcnames(List ps) { \
181  IMP_OBJECT_LOG; \
182  /* Bad things can happen if we use a Temp, as things get unreffed
183  before being reffed if they are in both lists */ \
184  clear_##lcnames(); \
185  add_##lcnames(ps); \
186  } \
187  /** \brief Must be the same set, just in a different order. */ template < \
188  class List> \
189  void set_##lcnames##_order(List ps) { \
190  IMP_OBJECT_LOG; \
191  IMP_USAGE_CHECK(ps.size() == lcname##_vector_.size(), \
192  "Reordered elements don't match."); \
193  lcname##_vector_.clear(); \
194  lcname##_vector_.insert(lcname##_vector_.end(), ps.begin(), ps.end()); \
195  } /** \brief add obj to the container
196  \return index of object within the object
197  */ \
198  unsigned int add_##lcname(Data obj) { \
199  IMP_OBJECT_LOG; \
200  unsigned int index = lcname##_vector_.size(); \
201  lcname##_vector_.push_back(obj); \
202  IMP_UNUSED(index); \
203  IMP_UNUSED(obj); \
204  OnAdd; \
205  lcname##_handle_change(); \
206  return index; \
207  } /** \brief Add several objects to the container. They are not necessarily \
208  added at the end. \
209  */ \
210  template <class List> \
211  void add_##lcnames(List objs) { \
212  IMP_OBJECT_LOG; \
213  unsigned int osz = lcname##_vector_.size(); \
214  lcname##_vector_.insert(lcname##_vector_.end(), objs.begin(), objs.end()); \
215  for (PluralData::size_type i = 0; i < objs.size(); ++i) { \
216  Data obj = lcname##_vector_[osz + i]; \
217  unsigned int index(osz + i); \
218  OnAdd; \
219  IMP_UNUSED(obj); \
220  IMP_UNUSED(index); \
221  } \
222  lcname##_handle_change(); \
223  } /** \brief Clear all objects from the container */ \
224  void clear_##lcnames() { \
225  lcname##_vector_.clear(); \
226  lcname##_handle_change(); \
227  } \
228  unsigned int get_number_of_##lcnames() const { \
229  return lcname##_vector_.size(); \
230  } /** \brief return true if there are any objects in the container*/ \
231  bool get_has_##lcnames() const { \
232  return !lcname##_vector_.empty(); \
233  } /** Get the object refered to by the index
234  \throws IndexException in Python if the index is out of range
235  */ \
236  Data get_##lcname(unsigned int i) const { return lcname##_vector_[i]; } \
237  PluralData get_##lcnames() const { \
238  return get_as<PluralData>(lcname##_vector_); \
239  } \
240  void reserve_##lcnames(unsigned int sz) { lcname##_vector_.reserve(sz); } \
241  IMP_EXPOSE_ITERATORS(PluralData, lcname##_vector_, Ucname, Ucnames, lcname, \
242  lcnames); \
243  \
244  protected: /** This method allows one to modify the contents of the container
245  without
246  any callbacks being made.*/ \
247  PluralData& mutable_access_##lcnames() { return lcname##_vector_; } \
248  IMP_NO_DOXYGEN(const PluralData& access_##lcnames() const { \
249  return lcname##_vector_; \
250  }) private : void lcname##_handle_remove(Data obj) { \
251  Ucname##DataWrapper::do_handle_remove(obj, this); \
252  } \
253  void lcname##_handle_change() { \
254  OnChanged; \
255  clear_caches(); \
256  } \
257  struct Ucname##DataWrapper : public PluralData { \
258  template <class TT> \
259  static void do_handle_remove(Data obj, TT* container) { \
260  IMP_UNUSED(container); \
261  IMP_UNUSED(obj); \
262  OnRemoved; \
263  } \
264  /* Older GCC (e.g. on Mac OS X 10.4) does not correctly export the
265  symbol for this destructor even when the surrounding class is itself
266  exported, causing lookup failures in DSOs that use the class.
267  Work around this by forcing the symbol to be exported. Ideally, we
268  should have a configure check for this problem... */ \
269  IMP_FORCE_EXPORT(~Ucname##DataWrapper()); \
270  }; \
271  friend struct Ucname##DataWrapper; \
272  IMP_NO_DOXYGEN(Ucname##DataWrapper lcname##_vector_;) \
273  IMP_PROTECTION(protection) IMP_REQUIRE_SEMICOLON_CLASS(list##lcname)
274 
275 #endif
276 
277 //! This should go in a .cpp file for the respective class.
278 /**
279  This code should go in a .cpp file. One macro for each IMP_CONTAINER.
280  \param[in] Class The name of the class containing this container.
281  \param[in] Ucname The name of the type of container in uppercase.
282  \param[in] lcname The name of the type of container in lower case.
283  \param[in] Data The type of the data to store.
284  \param[in] PluralData The plural of the data name. This should be a
285  container type.
286 
287  For all of these the current object is called obj and is of type Data.
288 */
289 #define IMP_LIST_IMPL(Class, Ucname, lcname, Data, PluralData) \
290  IMP_LIST_ACTION_IMPL(Class, Ucname, Ucname##s, lcname, lcname##s, Data, \
291  PluralData)
292 
293 #define IMP_LIST_ACTION_IMPL(Class, Ucname, Ucnames, lcname, lcnames, Data, \
294  PluralData) \
295  Class::Ucname##DataWrapper::~Ucname##DataWrapper() { \
296  for (unsigned int i = 0; i < size(); ++i) { \
297  do_handle_remove(operator[](i), static_cast<Class*>(0)); \
298  } \
299  } \
300  IMP_REQUIRE_SEMICOLON_NAMESPACE
301 
302 #define IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname) \
303  for (unsigned int _2 = 0; _2 < imp_foreach_indexes.size(); ++_2) { \
304  tname ContainerType::ContainedIndexType _1 = imp_foreach_indexes[_2]; \
305  bool imp_foreach_break = false; \
306  operation; \
307  if (imp_foreach_break) { \
308  break; \
309  } \
310  }
311 
312 #define IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, tname) \
313  IMPKERNEL_DEPRECATED_MACRO(2.2, \
314  "Use get_contents() and a IMP_FOREACH macro."); \
315  do { \
316  if (container->get_provides_access()) { \
317  const tname ContainerType::ContainedIndexTypes& imp_foreach_indexes = \
318  container->get_access(); \
319  IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname); \
320  } else { \
321  tname ContainerType::ContainedIndexTypes imp_foreach_indexes = \
322  container->get_indexes(); \
323  IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname); \
324  } \
325  } while (false)
326 
327 /** \see IMP_CONTAINER_FOREACH().
328 
329  This version is for use in a template function. See
330  IMP_FOREACH_INDEX() for another version.
331 */
332 #define IMP_CONTAINER_FOREACH_TEMPLATE(ContainerType, container, operation) \
333  IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, typename)
334 
335 /** These macros avoid various inefficiencies.
336 
337  The macros take the name of the container and the operation to
338  perform. In operation, _1 is used to refer to the item using its
339  ContainedIndexType (e.g., IMP::ParticleIndex in SingletonContainer,
340  or IMP::ParticleIndexPair in PairContainer).
341  The location of this item in the container itself is _2.
342  Use it like:
343  \code
344  IMP_CONTAINER_FOREACH(SingletonContainer, sc, std::cout << "Item "
345  << _2 << " has particle index " << _1 << std::endl);
346  \endcode
347 
348  See IMP_CONTAINER_FOREACH_TEMPLATE() if you want to use it in a template
349  function.
350 */
351 #define IMP_CONTAINER_FOREACH(ContainerType, container, operation) \
352  IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, )
353 
354 /** Provide a block that can have efficient, direct access to the contents
355  of the container in the variable imp_indexes.
356 */
357 #define IMP_CONTAINER_ACCESS(ContainerType, container, operation) \
358  do { \
359  if (container->get_provides_access()) { \
360  const ContainerType::ContainedIndexTypes& imp_indexes = \
361  container->get_access(); \
362  operation; \
363  } else { \
364  ContainerType::ContainedIndexTypes imp_indexes = \
365  container->get_indexes(); \
366  operation; \
367  } \
368  } while (false)
369 
370 #endif /* IMPKERNEL_CONTAINER_MACROS_H */
Helper macros for writing doxygen documentation.
Logging and error reporting support.
Checking and error reporting support.
Helper macros for throwing and handling exceptions.