iLab Neuromorphic Robotics Toolkit  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GenericBag.H
Go to the documentation of this file.
1 /*! @file
2  @author Shane Grant
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_DESIGN_GENERICBAG_H
36 #define INCLUDE_NRT_CORE_DESIGN_GENERICBAG_H
37 
42 #include <nrt/Core/Debugging/Log.H>
43 
44 #include <algorithm>
45 #include <cstdlib>
46 #include <string>
47 #include <type_traits>
48 
49 #include <nrt/External/cereal/access.hpp>
50 #include <nrt/External/cereal/types/base_class.hpp>
51 
52 namespace nrt
53 {
54  //! A class for holding a bag of unique, unordered components
55  /*! This class is designed to hold an unordered set of distinct type components.
56  Any two types are considered distinct if std::is_same<std::decay<T1>, std::decay<T2>>
57  would return false.
58 
59  Element access is provided by get<Type>(), which returns a reference to the first item of type Type in the bag as
60  ordered in the template parameter list. Since types should be distinct, the first instance of type Type will be
61  the only instance of type Type. Behavior is undefined if two of the same type occur in the bag.
62 
63  @code
64  nrt::GenericBag<int, float, double> bag(1, 2.0f, 3.0);
65  std::cout << bag.get<double>() << std::endl; // displays 3
66  @endcode
67 
68  Attempting to access a type that does not exist in the bag will result in a compile time error.
69 
70  In addition, a bag can be assigned to another bag of arbitrary type and only the fields in their intersection will
71  be assigned. In the case where no fields are shared, no operation will be carried out.
72 
73  @code
74  nrt::GenericBag<int, float, double> bag1(1, 2.0f, 3.0);
75  nrt::GenericBag<float, bool> bag2( 99.0f, true );
76 
77  bag1 = bag2;
78 
79  std::cout << bag1.get<float>() << std::endl; // displays 99
80  std::cout << bag1.get<int>() << std::endl; // displays 1
81  @endcode
82 
83  The ordering of elements does not matter for this operation.
84 
85  @code
86  nrt::GenericBag<int, bool> bag1;
87  nrt::GenericBag<bool, int> bag2;
88 
89  bag1 = bag2;
90  bag2 = bag1;
91  @endcode
92 
93  Explicit reference types can be used to specialize a GenericBag and can be accessed with or without specifying a
94  reference, since access to types only requires that the types are equivalent on a decayed basis.
95 
96  @code
97  int x = 3;
98  nrt::GenericBag<int&> bag1( x );
99  std::cout << bag1.get<int>() << " " << bag1.get<int&>() << std::endl;
100  // displays 3 3
101  @endcode
102 
103  In addition, types specified as a reference will be considered equivalent to non reference types during
104  assignment.
105 
106  Assignment in the other direction to a reference type will change the original source of the reference.
107 
108  @code
109  int x = 3;
110  nrt::GenricBag<int&> bag1( x );
111  nrt::GenericBag<int, double> bag2( 4, 3.7 );
112 
113  bag2 = bag1;
114 
115  std::cout << bag2.get<int>() << std::endl; // displays 3
116 
117  bag2.get<int>() = 33;
118 
119  bag1 = bag2;
120 
121  std::cout << x << std::endl; // displays 33
122  @endcode
123 
124  Note that since we only care about decayed types when getting types out or doing assignment, the following is
125  perfectly valid:
126 
127  @code
128  nrt::GenericBag<int, const bool> bag1( 3, true );
129  nrt::GenericBag<const int, bool> bag2 = bag1; // valid, const int matches with int, bool matches with const bool
130 
131  int x = 3;
132  nrt::GenericBag<const int &> bag3( x );
133 
134  int & y = bag3.get<int>(); // int will match to const int &, but this will be invalid since int & cannot be bound to const int &
135  @endcode
136 
137  @relates GenericBag
138  @ingroup design */
139  template <class FirstT, class ... OtherT>
140  class GenericBag<FirstT, OtherT...> : private GenericBag<OtherT...>
141  {
142  public:
143  //! @name Constructors
144  //! @{
145 
146  //! Initializes all elements to their default values
147  GenericBag();
148 
149  //! Initialize all elements to the provided values in the same order as
150  //! template parameters
151  /*! @param first Initial value for the first type
152  @param other Initial values in corresponding order to other types */
153  GenericBag( FirstT const & first, OtherT const & ... other );
154 
155  //! Explicit copy constructor
156  /*! All fields in common with the parameter bag will be set equal;
157  any fields not in common will be given default values.
158 
159  Two fields are considered equal if they match on an
160  std::is_same<std::decay<T1>, std::decay<T2>> basis */
161  template <class FirstT2, class ... OtherT2>
162  explicit GenericBag( GenericBag<FirstT2, OtherT2...> const & bag );
163 
164  //! Explicit copy from an empty bag
165  explicit GenericBag( GenericBag<> const & bag );
166 
167  //! Explicit copy constructor
168  /*! All fields in common with the parameter bag will be set equal;
169  any fields not in common will be given default values
170 
171  Two fields are considered equal if they match on an
172  std::is_same<std::decay<T1>, std::decay<T2>> basis */
173  template <class FirstT2, class ... OtherT2>
174  explicit GenericBag( StreamableGenericBag<FirstT2, OtherT2...> const & bag );
175 
176  //! Explicit copy from an empty bag
177  explicit GenericBag( StreamableGenericBag<> const & bag );
178 
179  //! Move constructor
180  /*! All fields in common with the parameter bag will be set equal;
181  any fields not in common will be given default values
182 
183  Two fields are considered equal if they match on an
184  std::is_same<std::decay<T1>, std::decay<T2>> basis */
185  template <class FirstT2, class ... OtherT2>
186  GenericBag( GenericBag<FirstT2, OtherT2...> && bag );
187 
188  //! Move from empty bag
189  GenericBag( GenericBag<> && bag );
190 
191  //! Move constructor
192  /*! All fields in common with the parameter bag will be set equal;
193  any fields not in common will be given default values
194 
195  Two fields are considered equal if they match on an
196  std::is_same<std::decay<T1>, std::decay<T2>> basis */
197  template <class FirstT2, class ... OtherT2>
198  GenericBag( StreamableGenericBag<FirstT2, OtherT2...> && bag );
199 
200  //! Move from empty bag
201  GenericBag( StreamableGenericBag<> && bag );
202 
203  //! @}
204  /*! @name Element Access and Type Information
205  Access to individual elements is possible by either specifying a specific
206  type, which will be matched on an std::decay basis, or a specific index in the
207  bag. */
208  //! @{
209 
210  //! Return a reference to the first instance of type T encountered in the bag
211  /*! This does not perform a strict search for type T. Instead, it looks for any instance of
212  std::decay<T>::type. The return type is then the most compatible reference type for
213  the matched type (e.g. an actual type of const int will return a const reference as opposed
214  to a non const reference). Errors may still be encountered if the ultimate type this is
215  assigned to is not compatible.
216 
217  @tparam T The loose type to match (matching is performed on an std::decay basis)
218  @return A reference (const if necessary) to the matched type */
219  template <class T>
220  typename getReferenceType<T, FirstT, OtherT...>::type get();
221 
222  //! Return a const reference to the first instance of type T
223  /*! This does not perform a strict search for type T. Instead it looks for any instance of
224  std::decay<T>::type. Errors may still be encountered if the types are not assignable. */
225  template <class T>
226  typename std::decay<T>::type const & get() const;
227 
228  //! Return a reference to the type at the given index
229  template <size_t Index>
230  typename getIndexType<Index, GenericBag>::type & get();
231 
232  //! Return a const reference to the type at the given index
233  template <size_t Index>
234  typename getIndexType<Index, GenericBag>::type const & get() const;
235 
236  //! Attempts to return a reference to the first instance of type T encountered in the bag
237  /*! This does not perform a strict search for type T. Instead, it looks for any instance of
238  std::decay<T>::type. The return type is then the most compatible reference type for
239  the matched type (e.g. an actual type of const int will return a const reference as opposed
240  to a non const reference). Errors may still be encountered if the ultimate type this is
241  assigned to is not compatible.
242 
243  @tparam T The loose type to match (matching is performed on an std::decay basis)
244  @return A reference (const if necessary) to the matched type
245 
246  @throws GenericBagException If the item does not exist */
247  template <class T>
248  typename getReferenceType<T, FirstT, OtherT...>::type tryGet();
249 
250  //! Attempts to get a constant reference to the first instance of Type T
251  /*! This does not perform a strict search for type T. Instead it looks for any instance of
252  std::decay<T>::type. Errors may still be encountered if the types are not assignable.
253 
254  @throws GenericBagException If the item does not exist */
255  template <class T>
256  typename std::decay<T>::type const & tryGet() const;
257 
258  //! Checks whether the GenericBag contains a certain type
259  /*! This does not perform a strict search for type T. Instead it looks for any instance of
260  std::decay<T>::type. */
261  template <class T>
262  bool const hasType() const;
263 
264  //! @}
265  //! @name Assignment Operators
266  //! @{
267 
268  //! Assignment operator
269  /*! All fields in common with the parameter bag will be set equal;
270  any fields not in common will be given default values
271 
272  Two fields are considered equal if they match on an
273  std::is_same<std::decay<T1>, std::decay<T2>> basis */
274  template <class FirstT2, class ... OtherT2>
275  GenericBag & operator=( GenericBag<FirstT2, OtherT2...> const & bag );
276 
277  //! Assign to empty bag
278  /*! All fields in common with the parameter bag will be set equal to the parameter's
279  * value for that field. All other fields will be left as is. */
280  GenericBag & operator=( GenericBag<> const & bag );
281 
282  //! Assignment operator
283  /*! All fields in common with the parameter bag will be set equal;
284  any fields not in common will be given default values
285 
286  Two fields are considered equal if they match on an
287  std::is_same<std::decay<T1>, std::decay<T2>> basis */
288  template <class FirstT2, class ... OtherT2>
289  GenericBag & operator=( StreamableGenericBag<FirstT2, OtherT2...> const & bag );
290 
291  //! Assign to empty bag
292  /*! All fields in common with the parameter bag will be set equal to the parameter's
293  * value for that field. All other fields will be left as is. */
294  GenericBag & operator=( StreamableGenericBag<> const & bag );
295 
296  //! Move Assignment operator
297  /*! All fields in common with the parameter bag will be set equal;
298  any fields not in common will be given default values
299 
300  Two fields are considered equal if they match on an
301  std::is_same<std::decay<T1>, std::decay<T2>> basis */
302  template <class FirstT2, class ... OtherT2>
303  GenericBag & operator=( GenericBag<FirstT2, OtherT2...> && bag );
304 
305  //! Move assign to empty bag
306  /*! All fields in common with the parameter bag will be set equal to the parameter's
307  * value for that field. All other fields will be left as is. */
308  GenericBag & operator=( GenericBag<> && bag );
309 
310  //! Move Assignment operator
311  /*! All fields in common with the parameter bag will be set equal;
312  any fields not in common will be given default values
313 
314  Two fields are considered equal if they match on an
315  std::is_same<std::decay<T1>, std::decay<T2>> basis */
316  template <class FirstT2, class ... OtherT2>
317  GenericBag & operator=( StreamableGenericBag<FirstT2, OtherT2...> && bag );
318 
319  //! Move assign to empty bag
320  /*! All fields in common with the parameter bag will be set equal to the parameter's
321  * value for that field. All other fields will be left as is. */
322  GenericBag & operator=( StreamableGenericBag<> && bag );
323 
324  //! @}
325  /*! @name Advanced operations
326  Advanced operations include things such as applying a function to each element
327  in a bag, either in place or returning a new bag of some type. */
328  //! @{
329 
330  //! Apply a function to every element in the bag
331  /*! This allows you to apply a generic function to each element in the bag.
332  The function should be encapsulated in a functor class that has
333  an operator() that accepts any templated type that could be found
334  in the bag. The return type of the function should be directly related
335  to the template type.
336 
337  If you wish to bind arguments to your function, this should be done in
338  the construction of your functor prior to passing it to this function.
339 
340  The current bag will be "unwound" into a parameter pack of its elements,
341  each one having had some functon applied to it. A new GenericBag will
342  then be constructed taking this unwound parameter pack as a construction
343  argument, so it must match the types (in order) specified in RetTypes...
344 
345  The function applied must be a 1:1 mapping from input types to output
346  types.
347 
348  @tparam Functor The functor to use
349  @tparam RetTypes The types of the resulting GenericBag
350 
351  @return A new GenericBag consisting of the function applied to each element in the original */
352  template <class ... RetTypes, class Functor>
353  GenericBag<RetTypes...> applyFunction( Functor && f ) const;
354 
355  //! Apply a function in place to every element in the bag
356  /*! The function applied should be generic such that it can handle any of the
357  types currently in the bag. See applyFunction for more information.
358 
359  @tparam Functor The functor to use */
360  template <class Functor>
361  void applyFunctionInPlace( Functor && f );
362 
363  //! @}
364 
365  protected:
366  template <class ... Args> friend class GenericBag;
367  template <size_t H, class RT, class D1, class D2> friend struct getClassFromBag;
368  template <size_t H, class RT, class D1, class D2> friend struct tryGetClassFromBag;
369  template <size_t H1, size_t H2, class FT2, class ... OT2> friend struct bagOuterSearch;
370  template <size_t H1, class A1, class D1, class D2> friend struct bagInnerSearch;
371  template <size_t H1, size_t H2, class FT2, class ... OT2> friend struct bagOuterSearchMove;
372  template <size_t H1, class A1, class D1, class D2> friend struct bagInnerSearchMove;
373  template <size_t H, class RT> friend struct getIndexFromBag;
374  template <class ... Args> friend class StreamableGenericBag;
375  template <size_t H> friend struct streamGenericBagContents;
376  friend struct initiateGenericBagStream;
377  template <size_t H, class F, class RT> friend struct expandAndApply;
378  template <class T, class ... Args> friend struct getReferenceType;
379  template <class D1, class D2, class ... Args> friend struct getReferenceTypeImpl;
380 
381  //! Make FirstT available to helper functionality
382  typedef FirstT FirstType;
383 
384  //! Returns the first element held by this GenericBag
385  FirstT & first();
386 
387  //! Const version of above
388  FirstT const & first() const;
389 
390  //! Returns the other elements held by this GenericBag
391  GenericBag<OtherT...> & other();
392 
393  //! Const version of above
394  GenericBag<OtherT...> const & other() const;
395 
396  //! Our 'height' in the bag
397  static constexpr int height = GenericBag<OtherT...>::height + 1;
398 
399  protected:
400  friend class cereal::access;
401  template <class Archive>
402  void serialize( Archive & ar )
403  {
404  ar( cereal::base_class<GenericBag<OtherT...>>( this ) );
405  ar( itsFirst );
406  }
407 
408  private:
409  //! The first element
410  FirstT itsFirst;
411 
412  }; // class GenericBag
413 
414  //! A GenericBag class that requires that each of its elements support streaming IO
415  /*! This class functions exactly as if it is a GenericBag while allowing convenient
416  access to its contents through streaming IO operations.
417 
418  This means that each class contained in the Bag must implement the overloaded
419  operators << and >> as expected by IOstream.
420 
421  It also means that data can easily be entered or retrieved from the bag using
422  these operations:
423 
424  @code
425  nrt::StreamableGenericBag<int, bool> bag( 3, true );
426  std::cout << bag; // displays 3; true
427  @endcode
428 
429  Individual fields are delimited by a semicolon (;)
430 
431  @relates StreamableGenericBag
432  @ingroup design */
433  template <class FirstT, class ... OtherT>
434  class StreamableGenericBag<FirstT, OtherT...> : public GenericBag<FirstT, OtherT...>
435  {
436  public:
437  //! Default value constructor
438  StreamableGenericBag();
439 
440  //! Initial value constructor
441  /*! @param first Initial value for the first type
442  @param other Initial values in corresponding order to other types */
443  StreamableGenericBag( FirstT const & first, OtherT const & ... other );
444 
445  //! Explicit copy constructor
446  template <class ... Types>
447  explicit StreamableGenericBag( GenericBag<Types...> const & bag );
448 
449  //! Explicit copy constructor
450  template <class ... Types>
451  explicit StreamableGenericBag( StreamableGenericBag<Types...> const & bag );
452 
453  //! Move construction
454  template <class ... Types>
455  explicit StreamableGenericBag( GenericBag<Types...> && bag );
456 
457  //! Move constructor
458  template <class ... Types>
459  explicit StreamableGenericBag( StreamableGenericBag<Types...> && bag );
460 
461  //! Inherit all assignment operators
462  using GenericBag<FirstT, OtherT...>::operator=;
463 
464  protected:
465  template <size_t H> friend struct streamGenericBagContents;
466  friend struct initiateGenericBagStream;
467 
468  friend class cereal::access;
469  template <class Archive>
470  void serialize( Archive & ar )
471  {
472  ar( cereal::base_class<GenericBag<FirstT, OtherT...>>( this ) );
473  }
474  };
475 
476  //! Human readable input to a StreamableGenericBag
477  /*! Input in the form of bag.at<0>(); bag.at<1>(); ... etc
478  @relates StreamableGenericBag */
479  template <class ... Args>
480  std::istream& operator>>( std::istream & in, StreamableGenericBag<Args...> & bag );
481 
482  //! Human readable output to a StreamableGenericBag
483  /*! Output in the form of bag.at<0>(); bag.at<1>(); ... etc
484  @relates StreamableGenericBag */
485  template <class ... Args>
486  std::ostream& operator<<( std::ostream & out, StreamableGenericBag<Args...> const & bag );
487 
488  namespace exception
489  {
490  //! An exception thrown by GenericBags at runtime for invalid type access
491  /*! @relates GenericBag */
493  {
494  public:
495  GenericBagException( std::string const & field ) noexcept;
496  ~GenericBagException() noexcept;
497  private:
498  std::string itsField;
499  std::string whatstring;
500  }; // class GenericBagException
501  } // namespace exception
502 
503 //! Implementation specific details
505 
506 } // namespace nrt
507 
508 #endif // INCLUDE_NRT_CORE_DESIGN_GENERICBAG_H