IMP logo
IMP Reference Guide  develop.5651aa123e,2024/07/20
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-2022 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 /* Provide Python list-like object for IMP_LIST, below */
55 /* This is a bit ugly here and in the output Python code because everything
56  in one %pythoncode ends up on a single line */
57 #ifdef SWIG
58 #define IMP_LIST_PYTHON_IMPL(lcname, lcnames, ucnames, Ucnames) \
59 %pythoncode %{ \
60  def __get_##lcnames(self): \
61 return IMP._list_util.VarList(getdimfunc=self.get_number_of_##lcnames, \
62 getfunc=self.get_##lcname, erasefunc=self.erase_##lcname, \
63 appendfunc=self.add_##lcname, extendfunc=self.add_##lcnames, \
64 clearfunc=self.clear_##lcnames, indexfunc=self._python_index_##lcname) \
65 %} \
66 %pythoncode %{ \
67  def __set_##lcnames(self, obj):\
68 IMP._list_util.set_varlist(self.##lcnames, obj) \
69 %} \
70 %pythoncode %{ \
71  def __del_##lcnames(self): IMP._list_util.del_varlist(self.##lcnames) \
72 %} \
73 %pythoncode %{ \
74  ##lcnames = property(__get_##lcnames, __set_##lcnames, __del_##lcnames,\
75 doc="List of ##ucnames") \
76 %}
77 
78 #elif defined(IMP_DOXYGEN)
79 #define IMP_LIST_PYTHON_IMPL(lcname, lcnames, ucnames, Ucnames) \
80 public: \
81  /** \brief A Python list of Ucnames \
82  @pythononlymember */ \
83  list lcnames;
84 #else
85 #define IMP_LIST_PYTHON_IMPL(lcname, lcnames, ucnames, Ucnames)
86 #endif
87 
88 /** \brief A macro to provide a uniform interface for storing lists of
89  objects.
90 
91  This macro is designed to be used in the body of an object to add
92  a set of methods for manipulating a list of contained objects. It adds
93  methods
94  - get_foo
95  - set_foo
96  - set_foos
97  - erase_foo
98  - foos_begin, foos_end
99  - remove_foo
100  - remove_foos
101  etc. where foo is the lcname provided. In Python, a 'foos' member
102  is also provided, which acts like a regular Python list (the above
103  methods can also be used if desired).
104 
105  \param[in] protection The level of protection for the container
106  (public, private).
107  \param[in] Ucname The name of the type of container in uppercase.
108  \param[in] lcname The name of the type of container in lower case.
109  \param[in] Data The type of the data to store.
110  \param[in] PluralData The plural of the data name. This should be a
111  container type.
112 
113  An accompanying IMP_LIST_IMPL must go in a \c .cpp file.
114 
115  \note This macro should be given an appropriate name and wrapped in a
116  doxygen comment block such as by starting with a C++ comment like
117  \verbatim
118  @name short description
119  longer description
120  @{
121  \endverbatim
122  and ending with one like
123  \verbatim
124  @}
125  \endverbatim
126 */
127 #define IMP_LIST(protection, Ucname, lcname, Data, PluralData) \
128  IMP_LIST_ACTION(protection, Ucname, Ucname##s, lcname, lcname##s, Data, \
129  PluralData, , , )
130 
131 #if defined(SWIG) || defined(IMP_DOXYGEN)
132 
133 #define IMP_LIST_ACTION(protection, Ucname, Ucnames, lcname, lcnames, Data, \
134  PluralData, OnAdd, OnChanged, OnRemoved) \
135  IMP_LIST_PYTHON_IMPL(lcname, lcnames, ucnames, Ucnames) \
136  public: \
137  void remove_##lcname(Data d); \
138  unsigned int _python_index_##lcname(Data d, unsigned int start, \
139  unsigned int stop); \
140  void remove_##lcnames(const PluralData& d); \
141  void set_##lcnames(const PluralData& ps); \
142  void set_##lcnames##_order(const PluralData& objs); \
143  unsigned int add_##lcname(Data obj); \
144  void add_##lcnames(const PluralData& objs); \
145  void clear_##lcnames(); \
146  unsigned int get_number_of_##lcnames() const; \
147  bool get_has_##lcnames(); \
148  Data get_##lcname(unsigned int i) const; \
149  PluralData get_##lcnames() const; \
150  void erase_##lcname(unsigned int i); \
151  void reserve_##lcnames(unsigned int sz)
152 
153 #else
154 /** A version of IMP_LIST() for types where the spelling of the plural is
155  irregular (eg geometry-> geometries) and where actions can be taken
156  upon addition and removal:
157  \param[in] protection The level of protection for the container
158  (public, private).
159  \param[in] Ucname The name of the type of container in uppercase.
160  \param[in] lcname The name of the type of container in lower case.
161  \param[in] Data The type of the data to store.
162  \param[in] PluralData The plural of the data name. This should be a
163  container type.
164  \param[in] OnAdd Code to modify the passed in object. The object is obj
165  and its index index.
166  \param[in] OnChanged Code to get executed when the container changes.
167  \param[in] OnRemoved Code to get executed when an object is removed.
168 */
169 #define IMP_LIST_ACTION(protection, Ucname, Ucnames, lcname, lcnames, Data, \
170  PluralData, OnAdd, OnChanged, OnRemoved) \
171  IMP_PROTECTION(protection) \
172  /** \brief Remove first occurrence of d from the container. */ \
173  void remove_##lcname(Data d) { \
174  IMP_OBJECT_LOG; \
175  bool found = false; \
176  for (Ucname##Iterator it = lcnames##_begin(); it != lcnames##_end(); \
177  ++it) { \
178  if (*it == d) { \
179  lcname##_handle_remove(*it); \
180  found = true; \
181  lcname##_vector_.erase(it); \
182  break; \
183  } \
184  } \
185  IMP_UNUSED(found); \
186  IMP_USAGE_CHECK(found, d << " not found in container: " \
187  << get_as<PluralData>(lcname##_vector_)); \
188  lcname##_handle_change(); \
189  } \
190  unsigned int _python_index_##lcname(Data d, unsigned int start, \
191  unsigned int stop) { \
192  bool found = false; \
193  unsigned int num_of = get_number_of_##lcnames(); \
194  start = std::min(start, num_of); \
195  stop = std::min(stop, num_of); \
196  unsigned int indx = start; \
197  for (Ucname##Iterator it = lcnames##_begin() + start; \
198  indx < stop; ++it, ++indx) { \
199  if (*it == d) { \
200  found = true; \
201  break; \
202  } \
203  } \
204  if (!found) { \
205  IMP_THROW(d << " is not in list", ValueException); \
206  } \
207  return indx; \
208  } \
209  /** \brief Remove the ith element in the container */ \
210  void erase_##lcname(unsigned int i) { \
211  Ucname##Iterator it = lcnames##_begin() + i; \
212  lcname##_handle_remove(*it); \
213  lcname##_vector_.erase(it); \
214  lcname##_handle_change(); \
215  } \
216  /** \brief Remove any occurrences for which f is true */ \
217  template <class F> \
218  void remove_##lcnames##_if(const F& f) { \
219  IMP_OBJECT_LOG; \
220  for (Ucname##Iterator it = lcnames##_begin(); it != lcnames##_end(); \
221  ++it) { \
222  if (f(*it)) lcname##_handle_remove(*it); \
223  } \
224  lcname##_vector_.erase( \
225  std::remove_if(lcname##_vector_.begin(), lcname##_vector_.end(), f), \
226  lcname##_vector_.end()); \
227  lcname##_handle_change(); \
228  } \
229  /** \brief Remove any occurrences of each item in d. */ \
230  template <class List> \
231  void remove_##lcnames(List d) { \
232  IMP_OBJECT_LOG; \
233  Vector<Data> ds(d.begin(), d.end()); \
234  std::sort(ds.begin(), ds.end()); \
235  for (unsigned int i = 0; i < ds.size(); ++i) { \
236  lcname##_handle_remove(ds[i]); \
237  } \
238  lcname##_vector_.erase( \
239  std::remove_if(lcname##_vector_.begin(), lcname##_vector_.end(), \
240  ::IMP::internal::list_contains(ds)), \
241  lcname##_vector_.end()); \
242  } \
243  /** \brief Set the contents of the container to ps removing all its current
244  contents.
245  */ \
246  template <class List> \
247  void set_##lcnames(List ps) { \
248  IMP_OBJECT_LOG; \
249  /* Bad things can happen if we use a Temp, as things get unreffed
250  before being reffed if they are in both lists */ \
251  clear_##lcnames(); \
252  add_##lcnames(ps); \
253  } \
254  /** \brief Must be the same set, just in a different order. */ template < \
255  class List> \
256  void set_##lcnames##_order(List ps) { \
257  IMP_OBJECT_LOG; \
258  IMP_USAGE_CHECK(ps.size() == lcname##_vector_.size(), \
259  "Reordered elements don't match."); \
260  lcname##_vector_.clear(); \
261  lcname##_vector_.insert(lcname##_vector_.end(), ps.begin(), ps.end()); \
262  } /** \brief add obj to the container
263  \return index of object within the object
264  */ \
265  unsigned int add_##lcname(Data obj) { \
266  IMP_OBJECT_LOG; \
267  unsigned int index = lcname##_vector_.size(); \
268  lcname##_vector_.push_back(obj); \
269  IMP_UNUSED(index); \
270  IMP_UNUSED(obj); \
271  OnAdd; \
272  lcname##_handle_change(); \
273  return index; \
274  } /** \brief Add several objects to the container. They are not necessarily \
275  added at the end. \
276  */ \
277  template <class List> \
278  void add_##lcnames(List objs) { \
279  IMP_OBJECT_LOG; \
280  unsigned int osz = lcname##_vector_.size(); \
281  lcname##_vector_.insert(lcname##_vector_.end(), objs.begin(), objs.end()); \
282  for (PluralData::size_type i = 0; i < objs.size(); ++i) { \
283  Data obj = lcname##_vector_[osz + i]; \
284  unsigned int index(osz + i); \
285  OnAdd; \
286  IMP_UNUSED(obj); \
287  IMP_UNUSED(index); \
288  } \
289  lcname##_handle_change(); \
290  } /** \brief Clear all objects from the container */ \
291  void clear_##lcnames() { \
292  lcname##_vector_.clear(); \
293  lcname##_handle_change(); \
294  } \
295  unsigned int get_number_of_##lcnames() const { \
296  return lcname##_vector_.size(); \
297  } /** \brief return true if there are any objects in the container*/ \
298  bool get_has_##lcnames() const { \
299  return !lcname##_vector_.empty(); \
300  } /** Get the object referfed to by the index
301  \throws IndexException in Python if the index is out of range
302  */ \
303  Data get_##lcname(unsigned int i) const { return lcname##_vector_[i]; } \
304  PluralData get_##lcnames() const { \
305  return get_as<PluralData>(lcname##_vector_); \
306  } \
307  void reserve_##lcnames(unsigned int sz) { lcname##_vector_.reserve(sz); } \
308  IMP_EXPOSE_ITERATORS(PluralData, lcname##_vector_, Ucname, Ucnames, lcname, \
309  lcnames); \
310  \
311  protected: /** This method allows one to modify the contents of the container
312  without
313  any callbacks being made.*/ \
314  PluralData& mutable_access_##lcnames() { return lcname##_vector_; } \
315  IMP_NO_DOXYGEN(const PluralData& access_##lcnames() const { \
316  return lcname##_vector_; \
317  }) private : void lcname##_handle_remove(Data obj) { \
318  Ucname##DataWrapper::do_handle_remove(obj, this); \
319  } \
320  void lcname##_handle_change() { \
321  OnChanged; \
322  clear_caches(); \
323  } \
324  struct Ucname##DataWrapper : public PluralData { \
325  template <class TT> \
326  static void do_handle_remove(Data obj, TT* container) { \
327  IMP_UNUSED(container); \
328  IMP_UNUSED(obj); \
329  OnRemoved; \
330  } \
331  /* Older GCC (e.g. on Mac OS X 10.4) does not correctly export the
332  symbol for this destructor even when the surrounding class is itself
333  exported, causing lookup failures in DSOs that use the class.
334  Work around this by forcing the symbol to be exported. Ideally, we
335  should have a configure check for this problem... */ \
336  IMP_FORCE_EXPORT(~Ucname##DataWrapper()); \
337  }; \
338  friend struct Ucname##DataWrapper; \
339  IMP_NO_DOXYGEN(Ucname##DataWrapper lcname##_vector_;) \
340  IMP_PROTECTION(protection) IMP_REQUIRE_SEMICOLON_CLASS(list##lcname)
341 
342 #endif
343 
344 //! This should go in a .cpp file for the respective class.
345 /**
346  This code should go in a .cpp file. One macro for each IMP_CONTAINER.
347  \param[in] Class The name of the class containing this container.
348  \param[in] Ucname The name of the type of container in uppercase.
349  \param[in] lcname The name of the type of container in lower case.
350  \param[in] Data The type of the data to store.
351  \param[in] PluralData The plural of the data name. This should be a
352  container type.
353 
354  For all of these the current object is called obj and is of type Data.
355 */
356 #define IMP_LIST_IMPL(Class, Ucname, lcname, Data, PluralData) \
357  IMP_LIST_ACTION_IMPL(Class, Ucname, Ucname##s, lcname, lcname##s, Data, \
358  PluralData)
359 
360 #define IMP_LIST_ACTION_IMPL(Class, Ucname, Ucnames, lcname, lcnames, Data, \
361  PluralData) \
362  Class::Ucname##DataWrapper::~Ucname##DataWrapper() { \
363  for (unsigned int i = 0; i < size(); ++i) { \
364  do_handle_remove(operator[](i), static_cast<Class*>(0)); \
365  } \
366  } \
367  IMP_REQUIRE_SEMICOLON_NAMESPACE
368 
369 #define IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname) \
370  for (unsigned int _2 = 0; _2 < imp_foreach_indexes.size(); ++_2) { \
371  tname ContainerType::ContainedIndexType _1 = imp_foreach_indexes[_2]; \
372  bool imp_foreach_break = false; \
373  operation; \
374  if (imp_foreach_break) { \
375  break; \
376  } \
377  }
378 
379 #define IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, tname) \
380  IMPKERNEL_DEPRECATED_MACRO(2.2, \
381  "Use get_contents() and a for loop."); \
382  do { \
383  if (container->get_provides_access()) { \
384  const tname ContainerType::ContainedIndexTypes& imp_foreach_indexes = \
385  container->get_access(); \
386  IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname); \
387  } else { \
388  tname ContainerType::ContainedIndexTypes imp_foreach_indexes = \
389  container->get_indexes(); \
390  IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname); \
391  } \
392  } while (false)
393 
394 /** \see IMP_CONTAINER_FOREACH().
395 
396  This version is for use in a template function. See
397  IMP_FOREACH_INDEX() for another version.
398 */
399 #define IMP_CONTAINER_FOREACH_TEMPLATE(ContainerType, container, operation) \
400  IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, typename)
401 
402 /** These macros avoid various inefficiencies.
403 
404  The macros take the name of the container and the operation to
405  perform. In operation, _1 is used to refer to the item using its
406  ContainedIndexType (e.g., IMP::ParticleIndex in SingletonContainer,
407  or IMP::ParticleIndexPair in PairContainer).
408  The location of this item in the container itself is _2.
409  Use it like:
410  \code
411  IMP_CONTAINER_FOREACH(SingletonContainer, sc, std::cout << "Item "
412  << _2 << " has particle index " << _1 << std::endl);
413  \endcode
414 
415  See IMP_CONTAINER_FOREACH_TEMPLATE() if you want to use it in a template
416  function.
417 */
418 #define IMP_CONTAINER_FOREACH(ContainerType, container, operation) \
419  IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, )
420 
421 /** Provide a block that can have efficient, direct access to the contents
422  of the container in the variable imp_indexes.
423 */
424 #define IMP_CONTAINER_ACCESS(ContainerType, container, operation) \
425  do { \
426  if (container->get_provides_access()) { \
427  const ContainerType::ContainedIndexTypes& imp_indexes = \
428  container->get_access(); \
429  operation; \
430  } else { \
431  ContainerType::ContainedIndexTypes imp_indexes = \
432  container->get_indexes(); \
433  operation; \
434  } \
435  } while (false)
436 
437 #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.