iLab Neuromorphic Robotics Toolkit  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Message.H
Go to the documentation of this file.
1 /*! @file
2  @author Laurent Itti
3  @copyright GNU Public License (GPL v3)
4  @section License
5  @verbatim
6  // ////////////////////////////////////////////////////////////////////////
7  // The iLab Neuromorphic Robotics Toolkit (NRT) //
8  // Copyright 2010-2012 by the University of Southern California (USC) //
9  // and the iLab at USC. //
10  // //
11  // iLab - University of Southern California //
12  // Hedco Neurociences Building, Room HNB-10 //
13  // Los Angeles, Ca 90089-2520 - USA //
14  // //
15  // See http://ilab.usc.edu for information about this project. //
16  // ////////////////////////////////////////////////////////////////////////
17  // This file is part of The iLab Neuromorphic Robotics Toolkit. //
18  // //
19  // The iLab Neuromorphic Robotics Toolkit is free software: you can //
20  // redistribute it and/or modify it under the terms of the GNU General //
21  // Public License as published by the Free Software Foundation, either //
22  // version 3 of the License, or (at your option) any later version. //
23  // //
24  // The iLab Neuromorphic Robotics Toolkit is distributed in the hope //
25  // that it will be useful, but WITHOUT ANY WARRANTY; without even the //
26  // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //
27  // PURPOSE. See the GNU General Public License for more details. //
28  // //
29  // You should have received a copy of the GNU General Public License //
30  // along with The iLab Neuromorphic Robotics Toolkit. If not, see //
31  // <http://www.gnu.org/licenses/>. //
32  // ////////////////////////////////////////////////////////////////////////
33  @endverbatim */
34 
35 #ifndef INCLUDE_NRT_CORE_BLACKBOARD_MESSAGE_H
36 #define INCLUDE_NRT_CORE_BLACKBOARD_MESSAGE_H
37 
39 #include <nrt/External/cereal/types/memory.hpp>
40 #include <nrt/External/cereal/types/polymorphic.hpp>
41 
42 // for splits
43 #include <tuple>
44 #include <list>
45 #include <nrt/Core/Util/Async.H>
46 
47 //! Declare a message variable as a name-value pair.
48 /*! This is necessary if you want to serialize you message into e.g. JSON. */
49 #define NRT_VAR(var) cereal::make_nvp(#var, var)
50 
51 namespace nrt
52 {
53  class MessageSubscriberCoreBase;
54  template <class Subscription> class MessageSubscriberCore;
55  template <class Posting> class MessagePosterCore;
56  template <class T> class SplitMessagePoster;
57  template <class T, bool ISMSG = std::is_base_of<nrt::MessageBase, T>::value> class SplitMessageSubscriber;
58 
59  /*! \defgroup message Message classes and functions
60 
61  Modules communicate with each other by passing Message objects, which can be any C++ type that derived from
62  nrt::MessageBase and that implementa a serialize function.
63 
64  Messages can be of three basic types:
65 
66  - nrt::Message<T> for messages that contain a simngle data value. The data can be accessed through the value()
67  read/write function of nrt::Message<T>. Canonical example is a message for an int, a float, or a GenericImage.
68  - nrt::CompositeMessage<Field1, Field2, ...> for messages that contain several data fields and which can be split
69  into several nrt::Message<T> objects, one per data field. This is the preferred type to use when creating
70  complex messages with several fields, when one expects that some users might be interested in only some of the
71  fields. One first uses some macros to define a unique type associated with each field, specifying the field's
72  name and its data type. One can then create the composite message out of a collection of fields. Canonical
73  example is a message for GPS data, containing several doubles for latitude, longitude, etc and accounting for
74  the fact that some users may only be interested in the longitde data (so it is advantageous to be able to split
75  the message into its constituent parts)
76  - General C++ classes that simply derive from nrt::MessageBase and that are serializable. This should be used only
77  for complex messages (more than one data member) which it would never make sense to split into their individual
78  members. A canonical example is hard to find except maybe in the internals of the Blackboard (hence, avoid this
79  construct as much as possible): for example, a parameter-changed message that contains a field for the parameter
80  name, the module, and the new parameter value should never be split as all three data values can only work
81  together as a unit.
82 
83  \ingroup blackboard */
84 
85  /*! @{ */ // **********************************************************************
86 
87  // ######################################################################
88  //! Macro to create a standard message
89  /*! \def NRT_DECLARE_MESSAGE(MessageClass)
90  \hideinitializer
91  Declares a class for the given message, inherits from nrt::MessageBase, and inherits from nrt::MessageTypesCreator
92  (which automatically declares commonly used message types dependent on the current message type).
93 
94  Use this as follows:
95 
96  @code
97  NRT_DECLARE_MESSAGE(MyMessage)
98  {
99  public:
100  int x, y; // whatever data fields you want here
101 
102  // Don't forget the serialization function
103  template <class Archive> inline void serialize(Archive & ar)
104  {
105  ar( x, y );
106  }
107  };
108  @endcode */
109  #define NRT_DECLARE_MESSAGE(MessageClass) \
110  class MessageClass : public nrt::MessageBase, public nrt::MessageTypesCreator<MessageClass>
111 
112  // ######################################################################
113  //! Base class for a message that can be passed from one Module to other(s) (via a Blackboard)
114  /*! Messages are containers for data that can be sent from one Module to others even if the Modules reside on
115  different machines. This is all handled by the internal machinery of the Blackboard and Module, so Message
116  creators only need to do the following to make a fully functional custom Message:
117 
118  - Create a new class which inherits from nrt::MessageBase
119  - Overload the description() method to return a useful description of your new message
120  - Overload the serialize() method to pack and unpack your Message's internal data
121 
122  Serializing Messages:
123 
124  In order to make a message capable of being transmitted to remote Modules,
125  you must define which data members need to be packaged into the byte
126  stream and shipped over the wire. This is done inside of the serialize
127  method using the Cereal serialization library. For example:
128 
129 @code
130 namespace MyNamespace
131 {
132  class MyMessage : public nrt::MessageBase
133  {
134  public:
135  int myData1;
136  std::string myData2;
137  nrt::Image<float> myImage;
138 
139  template<class Archive>
140  inline void serialize(Archive& ar)
141  {
142  ar( myData1, myData2, myImage );
143  }
144  };
145 }
146 @endcode
147 
148  This method is then called whenever your message type needs to be
149  serialized or de-serialized, and the function call on the Archive
150  will either pack or unpack your data appropriately. Any data type can
151  be stuffed into an Archive if it has a serialize() method defined.
152  There is a serialize method for every C++ built-in type, all of the
153  STL containers, as well as most of the NRT data structures. To make
154  your own custom data type serializable, just define a serialize()
155  method like the this one.
156 
157  See the Cereal library documentation for more advanced usage.
158 
159  TODO: Add cereal documentation link here */
161  {
162  public:
163  //! Constructor
164  MessageBase();
165 
166  //! Virtual destructor for safe inheritance
167  virtual ~MessageBase();
168 
169  //! A short description of the Message type.
170  /*! This is for logging purposes and does not contain any of the Message's data */
171  virtual std::string description() const;
172 
173  //! Is this message composite and can it be split into parts?
174  /*! By default, Message objects are atomic entities. But see CompositeMessage for messages that can be split into
175  their parts. */
176  static bool const isComposite = false;
177 
178  private:
179  template <class Posting> friend class MessagePosterCore;
180  template <class Subscription> friend class MessageSubscriberCore;
181 
182  //! Empty tuple for split posters, as by default messages cannot split
183  typedef typename std::tuple<> SplitPostersTuple;
184 
185  //! Empty tuple for split subscribers, as by default messages cannot split
186  typedef typename std::tuple<> SplitSubscribersTuple;
187 
188  //! No-op in base class
189  static std::shared_ptr<SplitPostersTuple>
190  createSubPosters(std::string const & module, std::string const & descriptor);
191 
192  //! No-op in base class
193  std::shared_ptr<SplitSubscribersTuple>
194  createSubSubscribers(nrt::MessageSubscriberCoreBase * parent,
195  std::string const & module, std::string const & descriptor);
196 
197  //! No-op in base class
198  template <class X>
199  void doSplitPost(SplitPostersTuple const & sp, std::list<std::future<X> > & fut) const;
200  };
201 
202  // ######################################################################
203  //! Free function to statically identify the message type and return it as string
204  template <class Msg>
205  std::string const MessageType();
206 
207  //! Specialization for void messages
208  template <>
209  std::string const MessageType<void>();
210 
211  // ######################################################################
212  //! Generic message template that contains one value
213  /*! The template argument can be anything that is serializable. Beware that the internal name used by the Blackboard
214  for this message is whatever the compiler thinks the message is, so, for example, avoid Message<nrt::byte> since
215  the compiler calls that a Message<unsigned char>. You can use nrt::MessageType() to check out how the compiler
216  calls one of your types after it substitutes various typedefs and such. */
217  template <class T>
218  class Message : public MessageBase, public nrt::MessageTypesCreator< Message<T> >
219  {
220  public:
221  //! Construct this Message with a default value
222  Message();
223 
224  //! Construct this Message with a copy of a value
225  Message(T const & val);
226 
227  //! Construct this Message from an existing shared_ptr to a value
228  Message(std::shared_ptr<T> val);
229 
230  //! Destructor
231  virtual ~Message();
232 
233  //! Access the value, read/write
234  /*! Note that doing an assignment here will do a deep copy of the value; use valuePtr() to access a shared_ptr to
235  the value instead. */
236  T & value();
237 
238  //! Access the value, read-only
239  /*! Note that doing an assignment here will do a deep copy of the value; use valuePtr() to access a shared_ptr to
240  the value instead. */
241  T const & value() const;
242 
243  //! Access the value shared_ptr, read/write
244  std::shared_ptr<T> valuePtr();
245 
246  //! Access the value, read-only
247  std::shared_ptr<T const> valuePtr() const;
248 
249  protected:
250  friend class cereal::access;
251 
252  //! Serialization
253  template <class Archive> void serialize(Archive & ar)
254  {
255  ar( value_ );
256  }
257 
258  private:
259  template <class Typ, bool ISMSG> friend class SplitMessageSubscriber;
260 
261  //! The single value that this Message contains.
262  std::shared_ptr<T> value_;
263  };
264 
265  // ######################################################################
266  //! Generic message data template used in messages that contain multiple fields/values
267  /*! This is used by CompositeMessage<...> */
268  template <class Field, class ... Tail>
269  class MessageData<Field, Tail ...> : public Field, public MessageData<Tail ...>
270  {
271  public:
272  template <class Field2>
273  typename std::enable_if<std::is_same<Field, Field2>::value, typename Field2::DataType &>::type
274  fieldValue();
275 
276  template <class Field2>
277  typename std::enable_if< ! std::is_same<Field, Field2>::value, typename Field2::DataType &>::type
278  fieldValue();
279 
280  template <class Field2>
281  typename std::enable_if<std::is_same<Field, Field2>::value, typename Field2::DataType const &>::type
282  fieldValue() const;
283 
284  template <class Field2>
285  typename std::enable_if< ! std::is_same<Field, Field2>::value, typename Field2::DataType const &>::type
286  fieldValue() const;
287 
288  template <class Field2>
289  typename std::enable_if<std::is_same<Field, Field2>::value,
290  typename std::shared_ptr<typename Field2::DataType> >::type
291  fieldValuePtr();
292 
293  template <class Field2>
294  typename std::enable_if< ! std::is_same<Field, Field2>::value,
295  typename std::shared_ptr<typename Field2::DataType> >::type
296  fieldValuePtr();
297 
298  template <class Field2>
299  typename std::enable_if<std::is_same<Field, Field2>::value,
300  typename std::shared_ptr<typename Field2::DataType const> >::type
301  fieldValuePtr() const;
302 
303  template <class Field2>
304  typename std::enable_if< ! std::is_same<Field, Field2>::value,
305  typename std::shared_ptr<typename Field2::DataType const> >::type
306  fieldValuePtr() const;
307 
308  template <class Field2>
309  typename std::enable_if<std::is_same<Field, Field2>::value, char const *>::type
310  fieldName() const;
311 
312  template <class Field2>
313  typename std::enable_if< ! std::is_same<Field, Field2>::value, char const *>::type
314  fieldName() const;
315 
316  template <class Field2>
317  typename std::enable_if<std::is_same<Field, Field2>::value, char const *>::type
318  fieldDescription() const;
319 
320  template <class Field2>
321  typename std::enable_if< ! std::is_same<Field, Field2>::value, char const *>::type
322  fieldDescription() const;
323  };
324 
325  //######################################################################
326  //! A composite message with multiple fields
327  /*! See the tutorial page on \ref pt_SplitMessages for explanations on how to use composite messages and how to split
328  them into their elementary parts */
329  template <class ... Fields>
330  class CompositeMessage : public MessageData<Fields ...>, public virtual nrt::MessageBase
331  {
332  public:
333  //! Type for our fields, as a tuple
334  typedef std::tuple<Fields ...> TupleType;
335 
336  //! Number of fields
337  constexpr static size_t msgsize = sizeof...(Fields);
338 
339  //! Tuple type of the underlying data types of all our fields
340  typedef std::tuple<typename Fields::DataType ...> DataTypes;
341 
342  //! Tuple type of our split posters
343  typedef std::tuple<std::shared_ptr<nrt::SplitMessagePoster<typename Fields::DataType> > ... >
345 
346  //! Tuple type of our split subscribers
347  typedef std::tuple<std::shared_ptr<nrt::SplitMessageSubscriber<typename Fields::DataType> > ... >
349 
350  //! We are a composite message (i.e., can be split)
351  static bool const isComposite = true;
352 
353  private:
354  template <class Posting> friend class MessagePosterCore;
355  template <class Subscription> friend class MessageSubscriberCore;
356 
357  inline static std::shared_ptr<SplitPostersTuple>
358  createSubPosters(std::string const & module, std::string const & descriptor);
359 
360  template <size_t I = 0> inline
361  static typename std::enable_if<I == msgsize, void>::type
362  createSubPostersInternal(SplitPostersTuple & sp, std::string const & module, std::string const & descriptor);
363 
364  template <size_t I = 0> inline
365  static typename std::enable_if<I < msgsize, void>::type
366  createSubPostersInternal(SplitPostersTuple & sp, std::string const & module, std::string const & descriptor);
367 
368 
369  template <size_t I = 0> inline
370  typename std::enable_if<I == msgsize, void>::type
371  doSplitPost(SplitPostersTuple const & sp, std::list<std::future<void> > & fut) const;
372 
373  template <size_t I = 0> inline
374  typename std::enable_if<I < msgsize, void>::type
375  doSplitPost(SplitPostersTuple const & sp, std::list<std::future<void> > & fut) const;
376 
377 
378  template <size_t I = 0> inline
379  typename std::enable_if<I == msgsize, void>::type
380  createSubSubscribersInternal(SplitSubscribersTuple & ss, nrt::MessageSubscriberCoreBase * parent,
381  std::string const & module, std::string const & descriptor);
382 
383  template <size_t I = 0> inline
384  typename std::enable_if<I < msgsize, void>::type
385  createSubSubscribersInternal(SplitSubscribersTuple & ss, nrt::MessageSubscriberCoreBase * parent,
386  std::string const & module, std::string const & descriptor);
387 
388  std::shared_ptr<SplitSubscribersTuple>
389  createSubSubscribers(nrt::MessageSubscriberCoreBase * parent, std::string const & module,
390  std::string const & descriptor);
391 
392  public:
393  //! Serialization (Base case - do nothing)
394  template <size_t I = 0, class Archive = void> inline
395  typename std::enable_if<I == msgsize, void>::type serialize(Archive & ar)
396  { }
397 
398  //! Serialization
399  template <size_t I = 0, class Archive = void> inline
400  typename std::enable_if<I < msgsize, void>::type serialize(Archive & ar)
401  {
402  typedef typename std::tuple_element<I, TupleType>::type IType;
403  ar( this->IType::value_ );
404  this->serialize<I+1>(ar);
405  }
406  };
407 
408 
409  // ######################################################################
410  //! An empty message used to trigger events
411  class TriggerMessage : public nrt::MessageBase, public nrt::MessageTypesCreator<TriggerMessage>
412  {
413  public:
414  //! Empty serialization function
415  template <class Archive> inline void serialize(Archive& ar) { }
416  };
417 
418  //! Convenience macro to define a MessageField
419  /*! \def NRT_DECLARE_MESSAGEFIELD(FieldName, Type, Description)
420  \hideinitializer
421  Declare a field to be used in a CompositeMessage. See the tutorial page on \ref pt_SplitMessages for explanations
422  on how to use composite messages and how to split them into their elementary parts */
423 
424  //! Convenience macro to access a MessageField
425  /*! \def NRT_MESSAGEFIELD(x)
426  \hideinitializer
427  Access a field declared using #NRT_DECLARE_MESSAGEFIELD(FieldName, Type, Description), to use it in a
428  CompositeMessage. See the tutorial page on \ref pt_SplitMessages for explanations on how to use composite messages
429  and how to split them into their elementary parts */
430 
431  /*! @} */ // **********************************************************************
432 
433 } // namespace nrt
434 
435 // Implementation details of no concern to end users:
437 
438 #endif // INCLUDE_NRT_CORE_BLACKBOARD_MESSAGE_H