IMP  2.1.1
The Integrative Modeling Platform
kernel/container_macros.h
Go to the documentation of this file.
1 /**
2  * \file IMP/kernel/container_macros.h
3  * \brief Macros to define containers of objects
4  *
5  * Copyright 2007-2013 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/kernel_config.h>
13 #include <IMP/base/check_macros.h>
14 #include <IMP/base/internal/Vector.h>
15 #include <IMP/base/SetCheckState.h>
16 #include <IMP/base/log_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  base::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::base::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 #ifndef SWIG
303 /** Report dependencies of the container Name. Add the line
304  deps_(new DependenciesScoreState(this), model) to the constructor
305  initializer list. The input_deps argument should add the input
306  containers to a variable ret.
307 */
308 #define IMP_CONTAINER_DEPENDENCIES(Name, input_deps) \
309  class DependenciesScoreState : public ScoreState { \
310  Name* back_; \
311  \
312  public: \
313  DependenciesScoreState(Name* n) \
314  : ScoreState(n->get_name() + " dependencies"), back_(n) {} \
315  ContainersTemp get_input_containers() const { \
316  ContainersTemp ret; \
317  input_deps return ret; \
318  } \
319  ContainersTemp get_output_containers() const { \
320  return ContainersTemp(1, back_); \
321  } \
322  ParticlesTemp get_input_particles() const { return ParticlesTemp(); } \
323  ParticlesTemp get_output_particles() const { return ParticlesTemp(); } \
324  void do_before_evaluate() {} \
325  void do_after_evaluate(DerivativeAccumulator*) {} \
326  IMP_OBJECT_METHODS( \
327  DependenciesScoreState) friend class DependenciesScoreState; \
328  ScopedScoreState deps_
329 
330 #else
331 #define IMP_CONTAINER_DEPENDENCIES(Name, input_deps)
332 #endif
333 
334 #define IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname) \
335  for (unsigned int _2 = 0; _2 < imp_foreach_indexes.size(); ++_2) { \
336  tname ContainerType::ContainedIndexType _1 = imp_foreach_indexes[_2]; \
337  bool imp_foreach_break = false; \
338  operation; \
339  if (imp_foreach_break) { \
340  break; \
341  } \
342  }
343 
344 #define IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, tname) \
345  do { \
346  if (container->get_provides_access()) { \
347  const tname ContainerType::ContainedIndexTypes& imp_foreach_indexes = \
348  container->get_access(); \
349  IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname); \
350  } else { \
351  tname ContainerType::ContainedIndexTypes imp_foreach_indexes = \
352  container->get_indexes(); \
353  IMP_CONTAINER_FOREACH_LOOP(ContainerType, container, operation, tname); \
354  } \
355  } while (false)
356 
357 /** See IMP_CONTAINER_FOREACH().
358 
359  This version is for use in a template function. See
360  IMP_FOREACH_INDEX() for another version.
361 */
362 #define IMP_CONTAINER_FOREACH_TEMPLATE(ContainerType, container, operation) \
363  IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, typename)
364 
365 /** These macros avoid various inefficiencies.
366 
367  The macros take the name of the container and the operation to
368  peform. In operation, _1 is used to refer to the item using its
369  ContainedIndexType (e.g., IMP::kernel::ParticleIndex in SingletonContainer,
370  or IMP::kernel::ParticleIndexPair in PairContainer).
371  The location of this item in the container itself is _2.
372  Use it like:
373  \code
374  IMP_CONTAINER_FOREACH(SingletonContainer, sc, std::cout << "Item "
375  << _2 << " has particle index " << _1 << std::endl);
376  \endcode
377 
378  See IMP_CONTAINER_FOREACH_TEMPLATE() if you want to use it in a template
379  function.
380 */
381 #define IMP_CONTAINER_FOREACH(ContainerType, container, operation) \
382  IMP_CONTAINER_FOREACH_IMPL(ContainerType, container, operation, )
383 
384 /** Provide a block that can have efficient, direct access to the contents
385  of the container in the variable imp_indexes.
386 */
387 #define IMP_CONTAINER_ACCESS(ContainerType, container, operation) \
388  do { \
389  if (container->get_provides_access()) { \
390  const ContainerType::ContainedIndexTypes& imp_indexes = \
391  container->get_access(); \
392  operation; \
393  } else { \
394  ContainerType::ContainedIndexTypes imp_indexes = \
395  container->get_indexes(); \
396  operation; \
397  } \
398  } while (false)
399 
400 #endif /* IMPKERNEL_CONTAINER_MACROS_H */
Various general useful macros for IMP.
Checkging and error reporting support.
Logging and error reporting support.
Exception definitions and assertions.