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