IMP logo
IMP Reference Guide  develop.3e83b1f578,2019/10/15
The Integrative Modeling Platform
decorator_macros.h
Go to the documentation of this file.
1 /**
2  * \file IMP/decorator_macros.h
3  * \brief Various general useful macros for IMP.
4  *
5  * Copyright 2007-2019 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef IMPKERNEL_DECORATOR_MACROS_H
10 #define IMPKERNEL_DECORATOR_MACROS_H
11 #include <IMP/kernel_config.h>
12 #include "particle_index.h"
13 #include "Particle.h"
14 #include "Decorator.h"
15 #include <IMP/check_macros.h>
16 #include <IMP/log_macros.h>
17 #include <IMP/showable_macros.h>
18 #include <IMP/warning_macros.h>
19 
20 
21 
22 /** Implement the needed methods for a decorator based on
23  - setup_particle()
24  - get_is_setup()
25  methods that you provide.
26 */
27 #define IMP_DECORATOR_METHODS(Name, Parent) \
28  public: \
29  /* Should be private but SWIG accesses it through the
30  comparison
31  macros*/ IMP_NO_DOXYGEN( \
32  typedef Parent ParentDecorator); \
33  Name() : Parent() {} \
34  Name(::IMP::Model *m, ::IMP::ParticleIndex id) \
35  : Parent(m, id) { \
36  IMP_INTERNAL_CHECK( \
37  get_is_setup(m, id), \
38  "Particle " << m->get_particle_name(id) \
39  << " missing required attributes for decorator " \
40  << #Name); \
41  } \
42  explicit Name(const IMP::ParticleAdaptor &d) : Parent(d) { \
43  IMP_INTERNAL_CHECK( \
44  get_is_setup(d.get_model(), d.get_particle_index()), \
45  "Particle " << d.get_model()->get_particle_name( \
46  d.get_particle_index()) \
47  << " missing required attributes for decorator " \
48  << #Name); \
49  } \
50  static bool get_is_setup(const IMP::ParticleAdaptor &p) { \
51  return get_is_setup(p.get_model(), p.get_particle_index()); \
52  } \
53  IMP_SHOWABLE(Name)
54 
55 
56 
57 /** Implement the needed methods for a decorator based on
58  - setup_particle()
59  - get_is_setup()
60  methods that you provide.
61 */
62 #define IMP_DECORATOR_WITH_TRAITS_METHODS(Name, Parent, TraitsType, \
63  traits_name, default_traits) \
64  private: \
65  TraitsType traits_; \
66  \
67  public: \
68  typedef TraitsType DecoratorTraits; \
69  const DecoratorTraits &get_decorator_traits() const { return traits_; } \
70  static const DecoratorTraits &get_default_decorator_traits() { \
71  static TraitsType dt = default_traits; \
72  return dt; \
73  } \
74  /* Should be private but SWIG accesses it through the
75  comparison
76  macros*/ IMP_NO_DOXYGEN( \
77  typedef Parent ParentDecorator); \
78  IMP_NO_DOXYGEN(typedef boost::true_type DecoratorHasTraits); \
79  Name() : Parent() {} \
80  Name(::IMP::Model *m, ::IMP::ParticleIndex id, \
81  const TraitsType &tr = default_traits) \
82  : Parent(m, id), traits_(tr) { \
83  IMP_INTERNAL_CHECK( \
84  get_is_setup(m, id, tr), \
85  "Particle " << m->get_particle_name(id) \
86  << " missing required attributes for decorator " \
87  << #Name); \
88  } \
89  explicit Name(const IMP::ParticleAdaptor &d, \
90  const TraitsType &tr = default_traits) \
91  : Parent(d), traits_(tr) { \
92  IMP_INTERNAL_CHECK( \
93  get_is_setup(d.get_model(), d.get_particle_index(), tr), \
94  "Particle " << d.get_model()->get_particle_name( \
95  d.get_particle_index()) \
96  << " missing required attributes for decorator " \
97  << #Name); \
98  } \
99  static bool get_is_setup(const IMP::ParticleAdaptor &p, \
100  const TraitsType &tr = default_traits) { \
101  return get_is_setup(p.get_model(), p.get_particle_index(), tr); \
102  } \
103  IMP_SHOWABLE(Name)
104 
105 /** Decorators need to be able to be set up from Particles, ParticleIndexes
106  and other Decorators. To help keep things uniform, we provide macros
107  to declare the setup functions. These macros expect that an appropriate
108  `do_setup_particle(Model *, ParticleIndex, args...)` function is
109  defined.
110 */
111 #define IMP_DECORATOR_SETUP_0(Name) \
112  /** Setup the particle so it can be used with this decorator. */ \
113  static Name setup_particle(Model *m, ParticleIndex pi) { \
114  IMP_USAGE_CHECK(!get_is_setup(m, pi), \
115  "Particle " << m->get_particle_name(pi) \
116  << " already set up as " << #Name); \
117  do_setup_particle(m, pi); \
118  return Name(m, pi); \
119  } \
120  static Name setup_particle(IMP::ParticleAdaptor decorator) { \
121  return setup_particle(decorator.get_model(), \
122  decorator.get_particle_index()); \
123  }
124 /** \see IMP_DECORATOR_SETUP_0() */
125 #define IMP_DECORATOR_SETUP_1(Name, FirstArgumentType, first_argument_name) \
126  /** Setup the particle so that it can be used with this decorator */ \
127  static Name setup_particle(Model *m, ParticleIndex pi, \
128  FirstArgumentType first_argument_name) { \
129  IMP_USAGE_CHECK(!get_is_setup(m, pi), \
130  "Particle " << m->get_particle_name(pi) \
131  << " already set up as " << #Name); \
132  do_setup_particle(m, pi, first_argument_name); \
133  return Name(m, pi); \
134  } \
135  /** \see setup_particle(m, pi, first_argument_name) */ \
136  static Name setup_particle(IMP::ParticleAdaptor decorator, \
137  FirstArgumentType first_argument_name) { \
138  return setup_particle(decorator.get_model(), \
139  decorator.get_particle_index(), \
140  first_argument_name); \
141  }
142 
143 /** \see IMP_DECORATOR_SETUP_0() */
144 #define IMP_DECORATOR_SETUP_2(Name, FirstArgumentType, first_argument_name, \
145  SecondArgumentType, second_argument_name) \
146  /** Setup the particle so it can be used with this decorator. */ \
147  static Name setup_particle(Model *m, ParticleIndex pi, \
148  FirstArgumentType first_argument_name, \
149  SecondArgumentType second_argument_name) { \
150  IMP_USAGE_CHECK(!get_is_setup(m, pi), \
151  "Particle " << m->get_particle_name(pi) \
152  << " already set up as " << #Name); \
153  do_setup_particle(m, pi, first_argument_name, second_argument_name); \
154  return Name(m, pi); \
155  } \
156  static Name setup_particle(IMP::ParticleAdaptor decorator, \
157  FirstArgumentType first_argument_name, \
158  SecondArgumentType second_argument_name) { \
159  return setup_particle(decorator.get_model(), \
160  decorator.get_particle_index(), first_argument_name, \
161  second_argument_name); \
162  }
163 /** \see IMP_DECORATOR_SETUP_0() */
164 #define IMP_DECORATOR_SETUP_3(Name, FirstArgumentType, first_argument_name, \
165  SecondArgumentType, second_argument_name, \
166  ThirdArgumentType, third_argument_name) \
167  /** Setup the particle so it can be used with this decorator. */ \
168  static Name setup_particle(Model *m, ParticleIndex pi, \
169  FirstArgumentType first_argument_name, \
170  SecondArgumentType second_argument_name, \
171  ThirdArgumentType third_argument_name) { \
172  IMP_USAGE_CHECK(!get_is_setup(m, pi), \
173  "Particle " << m->get_particle_name(pi) \
174  << " already set up as " << #Name); \
175  do_setup_particle(m, pi, first_argument_name, second_argument_name, \
176  third_argument_name); \
177  return Name(m, pi); \
178  } \
179  static Name setup_particle(IMP::ParticleAdaptor decorator, \
180  FirstArgumentType first_argument_name, \
181  SecondArgumentType second_argument_name, \
182  ThirdArgumentType third_argument_name) { \
183  return setup_particle(decorator.get_model(), \
184  decorator.get_particle_index(), first_argument_name, \
185  second_argument_name, third_argument_name); \
186  }
187 /** \see IMP_DECORATOR_SETUP_0() */
188 #define IMP_DECORATOR_SETUP_4(Name, FirstArgumentType, first_argument_name, \
189  SecondArgumentType, second_argument_name, \
190  ThirdArgumentType, third_argument_name, \
191  FourthArgumentType, fourth_argument_name) \
192  /** Setup the particle so it can be used with this decorator. */ \
193  static Name setup_particle(Model *m, ParticleIndex pi, \
194  FirstArgumentType first_argument_name, \
195  SecondArgumentType second_argument_name, \
196  ThirdArgumentType third_argument_name, \
197  FourthArgumentType fourth_argument_name) { \
198  IMP_USAGE_CHECK(!get_is_setup(m, pi), \
199  "Particle " << m->get_particle_name(pi) \
200  << " already set up as " << #Name); \
201  do_setup_particle(m, pi, first_argument_name, second_argument_name, \
202  third_argument_name, fourth_argument_name); \
203  return Name(m, pi); \
204  } \
205  static Name setup_particle(IMP::ParticleAdaptor decorator, \
206  FirstArgumentType first_argument_name, \
207  SecondArgumentType second_argument_name, \
208  ThirdArgumentType third_argument_name, \
209  FourthArgumentType fourth_argument_name) { \
210  return setup_particle(decorator.get_model(), \
211  decorator.get_particle_index(), first_argument_name, \
212  second_argument_name, third_argument_name, \
213  fourth_argument_name); \
214  }
215 
216 /** Decorators need to be able to be set up from Particles, ParticleIndexes
217  and other Decorators. To help keep things uniform, we provide macros
218  to declare the setup functions. These macros expect that an appropriate
219  `do_setup_particle(Model *, ParticleIndex, args...)` function is
220  defined. But any docs needed before the macro invocation.
221 */
222 #define IMP_DECORATOR_TRAITS_SETUP_0(Name) \
223  /** Setup the particle so it can be used with this decorator. */ \
224  static Name setup_particle( \
225  Model *m, ParticleIndex pi, \
226  DecoratorTraits tr = get_default_decorator_traits()) { \
227  do_setup_particle(m, pi, tr); \
228  return Name(m, pi, tr); \
229  } \
230  static Name setup_particle( \
231  IMP::ParticleAdaptor d, \
232  DecoratorTraits tr = get_default_decorator_traits()) { \
233  do_setup_particle(d.get_model(), d.get_particle_index(), tr); \
234  return Name(d.get_model(), d.get_particle_index(), tr); \
235  }
236 /** \see IMP_DECORATOR_TRAITS_SETUP_0() */
237 #define IMP_DECORATOR_TRAITS_SETUP_1(Name, FirstArgumentType, \
238  first_argument_name) \
239  static Name setup_particle( \
240  Model *m, ParticleIndex pi, \
241  FirstArgumentType first_argument_name, \
242  DecoratorTraits tr = get_default_decorator_traits()) { \
243  do_setup_particle(m, pi, first_argument_name, tr); \
244  return Name(m, pi, tr); \
245  } \
246  static Name setup_particle( \
247  IMP::ParticleAdaptor d, FirstArgumentType first_argument_name, \
248  DecoratorTraits tr = get_default_decorator_traits()) { \
249  do_setup_particle(d.get_model(), d.get_particle_index(), \
250  first_argument_name, tr); \
251  return Name(d.get_model(), d.get_particle_index(), tr); \
252  }
253 /** \see IMP_DECORATOR_TRAITS_SETUP_0() */
254 #define IMP_DECORATOR_TRAITS_SETUP_2(Name, FirstArgumentType, \
255  first_argument_name, SecondArgumentType, \
256  second_argument_name) \
257  static Name setup_particle( \
258  Model *m, ParticleIndex pi, \
259  FirstArgumentType first_argument_name, \
260  SecondArgumentType second_argument_name, \
261  DecoratorTraits tr = get_default_decorator_traits()) { \
262  do_setup_particle(m, pi, first_argument_name, second_argument_name, tr); \
263  return Name(m, pi, tr); \
264  } \
265  static Name setup_particle( \
266  IMP::ParticleAdaptor d, FirstArgumentType first_argument_name, \
267  SecondArgumentType second_argument_name, \
268  DecoratorTraits tr = get_default_decorator_traits()) { \
269  do_setup_particle(d.get_model(), d.get_particle_index(), \
270  first_argument_name, second_argument_name, tr); \
271  return Name(d.get_model(), d.get_particle_index(), tr); \
272  }
273 
274 //! Perform actions dependent on whether a particle has an attribute.
275 /** A common pattern is to check if a particle has a particular attribute,
276  do one thing if it does and another if it does not. This macro implements
277  that pattern. It requires that the method get_particle_index() return the
278  particle being used.
279 
280  \param[in] AttributeKey The key for the attribute
281  \param[in] Type The type for the attribute ("Int", "Float", "String")
282  \param[in] has_action The action to take if the Particle has the attribute.
283  The attribute value is stored in the variable VALUE.
284  \param[in] not_has_action The action to take if the Particle does not have
285  the attribute.
286  \see IMP_DECORATOR_GET()
287  \see IMP_DECORATOR_GET_SET()
288 
289 */
290 #define IMP_DECORATOR_GET(AttributeKey, Type, has_action, not_has_action) \
291  do { \
292  if (get_model()->get_has_attribute(AttributeKey, get_particle_index())) { \
293  Type VALUE = \
294  get_model()->get_attribute(AttributeKey, get_particle_index()); \
295  has_action; \
296  } else { \
297  not_has_action; \
298  } \
299  } while (false)
300 
301 //! Set an attribute, creating it if it does not already exist.
302 /** Another common pattern is to have an assumed value if the attribute
303  is not there. Then, you sometimes need to set the value whether it
304  is there or not.
305  \see IMP_DECORATOR_GET()
306  \see IMP_DECORATOR_GET_SET()
307 */
308 #define IMP_DECORATOR_SET(AttributeKey, value) \
309  do { \
310  if (get_model()->get_has_attribute(AttributeKey, get_particle_index())) { \
311  get_model()->set_attribute(AttributeKey, get_particle_index(), value); \
312  } else { \
313  get_model()->add_attribute(AttributeKey, get_particle_index(), value); \
314  } \
315  } while (false)
316 
317 //! Define methods for getting and setting a particular simple field
318 /** This macro defines methods to get and set a particular attribute.
319 
320  \param[in] name The lower case name of the attribute
321  \param[in] AttributeKey The AttributeKey object controlling
322  the attribute.
323  \param[in] Type The type of the attribute (upper case).
324  \param[in] ReturnType The type to return from the get.
325  \see IMP_DECORATOR_GET()
326  \see IMP_DECORATOR_SET()
327 */
328 #define IMP_DECORATOR_GET_SET(name, AttributeKey, Type, ReturnType) \
329  /** returns the value of the name attribute */ \
330  ReturnType get_##name() const { \
331  return static_cast<ReturnType> \
332  (get_model()->get_attribute(AttributeKey, get_particle_index())); \
333  } \
334  /** sets the value of the name attribute to t */ \
335  void set_##name(ReturnType t) { \
336  get_model()->set_attribute(AttributeKey, get_particle_index(), t); \
337  } \
338  IMP_REQUIRE_SEMICOLON_CLASS(getset##name)
339 
340 //! Define methods for getting and setting an optional simple field.
341 /** See IMP_DECORATOR_GET_SET(). The difference is that here you can provide
342  a default value to use if the decorator does not have the attribute.
343 
344  \param[in] name The lower case name of the attribute
345  \param[in] AttributeKey The expression to get the required attribute key.
346  \param[in] Type The type of the attribute (upper case).
347  \param[in] ReturnType The type to return from the get.
348  \param[in] default_value The value returned if the attribute is missing.
349 */
350 #define IMP_DECORATOR_GET_SET_OPT(name, AttributeKey, Type, ReturnType, \
351  default_value) \
352  /** returns the value of the name attribute, or default_value if \
353  the name attribute is missing */ \
354  ReturnType get_##name() const { \
355  IMP_DECORATOR_GET(AttributeKey, Type, \
356  return static_cast<ReturnType>(VALUE), \
357  return default_value); \
358  } \
359  /** sets the name attribute to t */ \
360  void set_##name(ReturnType t) { IMP_DECORATOR_SET(AttributeKey, t); } \
361  IMP_REQUIRE_SEMICOLON_CLASS(getset_##name)
362 
363 #define IMP_DECORATORS_DECL(Name, PluralName) \
364  class Name; \
365  typedef IMP::Vector<Name> PluralName
366 
367 #ifndef IMP_DOXYGEN
368 #define IMP_DECORATORS_DEF(Name, PluralName) \
369  /* needed so there is no ambiguity with operator->*/ \
370  inline std::ostream &operator<<(std::ostream &out, Name n) { \
371  n.show(out); \
372  return out; \
373  }
374 #else
375 #define IMP_DECORATORS_DEF(Name, PluralName)
376 #endif
377 
378 //! Define the types for storing sets of decorators
379 /** The macro defines the types PluralName and PluralNameTemp.
380  Parent is unused and remains for backward compatibility
381  */
382 #define IMP_DECORATORS(Name, PluralName, Parent) \
383  IMP_DECORATORS_DECL(Name, PluralName); \
384  IMP_DECORATORS_DEF(Name, PluralName)
385 
386 //! Define the types for storing sets of decorators
387 /** The macro defines the types PluralName and PluralNameTemp.
388  */
389 #define IMP_DECORATORS_WITH_TRAITS(Name, PluralName, Parent) \
390  /* needed so there is no ambiguity with operator->*/ \
391  inline std::ostream &operator<<(std::ostream &out, Name n) { \
392  n.show(out); \
393  return out; \
394  } \
395  typedef IMP::Vector<Name> PluralName
396 
397 
398 /**
399  Declares Decorator methods that allows (privately) setting a constraint
400  and publicly getting that constraint
401  */
402 #define IMP_CONSTRAINT_DECORATOR_DECL(Name) \
403  private: \
404  static ObjectKey get_constraint_key(); \
405  /** set a constraint associated with this decorator that applies 'before'
406  and 'after' before and after evaluation. The constraint is added as
407  a model ScoreState. If before and after are Null, the constraint is
408  reset and removed from the model list of score states.
409  */ \
410  static void set_constraint(SingletonModifier* before, \
411  SingletonDerivativeModifier* after, Model* m, \
412  ParticleIndex pi); \
413  \
414  public: \
415  Constraint* get_constraint() const { \
416  return dynamic_cast<Constraint*>( \
417  get_particle()->get_value(get_constraint_key())); \
418  } \
419  IMP_REQUIRE_SEMICOLON_CLASS(constraint)
420 
421 /**
422  Defines Decorator methods that allows (privately) setting a constraint
423  and publicly getting that constraint. The constraint is added as a
424  score state to the model.
425  */
426 #define IMP_CONSTRAINT_DECORATOR_DEF(Name) \
427  ObjectKey Name::get_constraint_key() { \
428  static ObjectKey ret(#Name " score state"); \
429  return ret; \
430  } \
431  void Name::set_constraint(SingletonModifier* before, \
432  SingletonDerivativeModifier* after, Model* m, \
433  ParticleIndex pi) { \
434  if (!after && !before) { \
435  if (m->get_has_attribute(get_constraint_key(), pi)) { \
436  m->remove_score_state(dynamic_cast<ScoreState*>( \
437  m->get_attribute(get_constraint_key(), pi))); \
438  m->remove_attribute(get_constraint_key(), pi); \
439  } \
440  } else { \
441  Constraint* ss = new core::SingletonConstraint( \
442  before, after, m, pi, \
443  std::string(#Name "updater for ") + m->get_particle_name(pi)); \
444  m->add_attribute(get_constraint_key(), pi, ss); \
445  m->add_score_state(ss); \
446  } \
447  } \
448  IMP_REQUIRE_SEMICOLON_NAMESPACE
449 
450 
451 /** Register a function that can be used to check that the particle
452  is valid with respect to the decorator. The function should take
453  a Particle* as an argument and return a bool. It should throw
454  an exception if something is wrong.
455 
456  This macro should only be used in a .cpp file.
457 */
458 #define IMP_CHECK_DECORATOR(Name, function) \
459  IMP::internal::ParticleCheck Name##pc(Name::get_is_setup, function);
460 
461 
462 //! Create a decorator that computes some sort of summary info on a set
463 /** Examples include a centroid or a cover for a set of particles.
464 
465  \param[in] Name The name for the decorator
466  \param[in] Parent the parent decorator type
467  \param[in] Members the way to pass a set of particles in
468  \param[in] SetupDoc extra documentation for setup
469 */
470 #define IMP_SUMMARIZE_DECORATOR_DECL(Name, Parent, Members, SetupDoc) \
471  class IMPCOREEXPORT Name : public Parent { \
472  IMP_CONSTRAINT_DECORATOR_DECL(Name); \
473  private: \
474  /** Sets up Name over particles in pis */ \
475  static void do_setup_particle(Model *m, ParticleIndex pi, \
476  const ParticleIndexes &pis); \
477  /** Sets up Name over particles passed by applying the refiner
478  over the particle pi
479  */ \
480  static void do_setup_particle(Model *m, ParticleIndex pi, \
481  Refiner *ref); \
482  \
483  public: \
484  IMP_DECORATOR_METHODS(Name, Parent); \
485  /** Sets up Name over members, and constrains Name to be
486  computed before model evaluation and to propagate derivatives
487  following model evaluation.
488  SetupDoc
489  */ \
490  IMP_DECORATOR_SETUP_1(Name, ParticleIndexesAdaptor, members); \
491  /** Sets up Name over particles passed by applying the refiner
492  over the particle pi, and constrains Name to be computed before
493  model evaluation and to propagate derivatives following model
494  evaluation.
495  SetupDoc
496  */ \
497  IMP_DECORATOR_SETUP_1(Name, Refiner *, refiner); \
498  static bool get_is_setup(Model *m, ParticleIndex pi) { \
499  return m->get_has_attribute(get_constraint_key(), pi); \
500  } \
501  IMP_NO_DOXYGEN(typedef boost::false_type DecoratorHasTraits); \
502  \
503  private: \
504  /* hide set methods*/ \
505  void set_coordinates() {}; \
506  void set_coordinates_are_optimized() const {} \
507  void set_coordinate() const {} \
508  void set_radius() const {} \
509  }; \
510  IMP_DECORATORS(Name, Name##s, Parent##s)
511 
512 
513 /** \see IMP_SUMMARIZE_DECORATOR_DECL()
514  \param[in] Name The name for the decorator
515  \param[in] Parent the parent decorator type
516  \param[in] Members the way to pass a set of particles in
517  \param[in] create_pre_modifier the statements to create the
518  SingletonModifier which computes the summary info,
519  using refiner 'ref'
520  \param[in] create_post_modifier a SingletonDerivativeModifier for
521  the derivatives of the summary back to its members,
522  using refiner 'ref'
523 */
524 #define IMP_SUMMARIZE_DECORATOR_DEF(Name, Parent, Members, \
525  create_pre_modifier, \
526  create_post_modifier) \
527  void Name::do_setup_particle(Model *m, ParticleIndex pi, \
528  const ParticleIndexes &pis) { \
529  Refiner *ref = new FixedRefiner(IMP::get_particles(m, pis)); \
530  SingletonModifier* pre_mod = create_pre_modifier; \
531  SingletonDerivativeModifier* post_mod = create_post_modifier; \
532  if (!Parent::get_is_setup(m, pi)) Parent::setup_particle(m, pi); \
533  set_constraint(pre_mod, post_mod, m, pi); \
534  } \
535  \
536  void Name::do_setup_particle(Model *m, ParticleIndex pi, \
537  Refiner *ref) { \
538  SingletonModifier* pre_mod = create_pre_modifier; \
539  SingletonDerivativeModifier* post_mod = create_post_modifier; \
540  if (!Parent::get_is_setup(m, pi)) Parent::setup_particle(m, pi); \
541  set_constraint(pre_mod, post_mod, m, pi); \
542  } \
543  \
544  void Name::show(std::ostream &out) const { \
545  out << #Name << " at " << static_cast<Parent>(*this); \
546  } \
547  IMP_CONSTRAINT_DECORATOR_DEF(Name)
548 
549 #endif /* IMPKERNEL_DECORATOR_MACROS_H */
The base class for decorators.
Various general useful functions for IMP.
Various general useful macros for IMP.
Logging and error reporting support.
Classes to handle individual model particles. (Note that implementation of inline functions is in int...
Exception definitions and assertions.
Various general useful macros for IMP.