Changeset 126:abc5db1233c8 in finroc_plugins_data_ports


Ignore:
Timestamp:
22.05.2017 01:34:21 (2 years ago)
Author:
Tobias Föhst <foehst@…>
Branch:
17.03
Phase:
public
Rebase:
62653330613263653936313863323438366631643662373766666265396264646361373266326563
Message:

Reimplements tPortPack via std::tuple for proper initialization, typed access and access via integer sequences

Files:
2 edited

Legend:

Unmodified
Added
Removed
  • tPortPack.h

    r125 r126  
    4040// External includes (system with <>, local with "") 
    4141//---------------------------------------------------------------------- 
     42#include "core/tFrameworkElement.h" 
     43#include "core/log_messages.h" 
     44#include "rrlib/util/tIteratorRange.h" 
     45#include "rrlib/util/tIntegerSequence.h" 
     46 
    4247#include "rrlib/util/tTypeList.h" 
    43 #include "core/tFrameworkElement.h" 
    4448 
    4549//---------------------------------------------------------------------- 
     
    6367//---------------------------------------------------------------------- 
    6468//! A group of several ports with different types. 
     69/*! 
     70 * This class creates a tuple of instances of the given port template 
     71 * inserting the given types.  It also provides methods to access the 
     72 * included ports and their change flags at runtime. 
     73 * 
     74 * \param TPort    A port class template to use for every packed port 
     75 * \param TTypes   A variadic list of the data types used in the ports 
     76 */ 
     77template <template <typename> class TPort, typename ... TTypes> 
     78class tPortPack 
     79{ 
     80  using tPorts = std::tuple<std::unique_ptr<TPort<TTypes>>...>; 
     81  using tIndex = typename rrlib::util::tIntegerSequenceGenerator<sizeof...(TTypes)>::type; 
     82 
     83//---------------------------------------------------------------------- 
     84// Public methods and typedefs 
     85//---------------------------------------------------------------------- 
     86public: 
     87 
     88  /*! Ctor with common port name prefix 
     89   * 
     90   * The port names will follow the scheme "<prefix> [0..]" 
     91   * 
     92   * \param parent        The parent framework element 
     93   * \param name_prefix   The common prefix used for all port names 
     94   */ 
     95  inline tPortPack(core::tFrameworkElement *parent, const std::string &name_prefix) 
     96  { 
     97    this->DispatchInitializer(parent, tIndex(), [&name_prefix](size_t i) 
     98    { 
     99      return name_prefix + std::to_string(i); 
     100    }); 
     101  } 
     102 
     103  /*! Ctor with custom port names 
     104   * 
     105   * The port names are completely taken from two iterators 
     106   * 
     107   * \param parent        The parent framework element 
     108   * \param names_begin   Begin of the list of port names 
     109   * \param names_end     End of the list of port names 
     110   */ 
     111  template <typename TIterator> 
     112  inline tPortPack(core::tFrameworkElement *parent, TIterator names_begin, TIterator names_end) 
     113  { 
     114    auto number_of_names = std::distance(names_begin, names_end); 
     115    if (number_of_names != sizeof...(TTypes)) 
     116    { 
     117      FINROC_LOG_THROW(rrlib::util::tTraceableException<std::logic_error>("Number of ports names (" + std::to_string(number_of_names) + ") does not fit given number of ports (" + std::to_string(sizeof...(TTypes)) + ")")); 
     118    } 
     119    this->DispatchInitializer(parent, tIndex(), [names_begin](size_t i) mutable { return *names_begin++; }); 
     120  } 
     121 
     122  /*! Ctor with custom port names (range) 
     123   * 
     124   * The port names are completely specified 
     125   * 
     126   * \param parent   The parent framework element 
     127   * \param names    A range of port names to be used 
     128   */ 
     129  template <typename TIterator> 
     130  inline tPortPack(core::tFrameworkElement *parent, const rrlib::util::tIteratorRange<TIterator> &names) : 
     131    tPortPack(parent, names.begin(), names.end()) 
     132  {} 
     133 
     134  /*! The number of ports in this port pack 
     135   * 
     136   * \returns Number of ports in \a this pack 
     137   */ 
     138  inline static constexpr size_t NumberOfPorts() 
     139  { 
     140    return sizeof...(TTypes); 
     141  } 
     142 
     143  /*! Access a specific port (strong type version, compile time) 
     144   * 
     145   * \param Tindex   The index of the port in this pack 
     146   * 
     147   * \return The specified port 
     148   */ 
     149  template <size_t Tindex> 
     150  inline typename std::tuple_element<Tindex, std::tuple<TPort<TTypes>...>>::type &GetPort() 
     151  { 
     152    static_assert(Tindex < this->NumberOfPorts(), "Port index not in range"); 
     153    return *std::get<Tindex>(this->ports); 
     154  } 
     155 
     156  /*! Access a specific port (runtime version) 
     157   * 
     158   * \param index   The index of the port in this pack 
     159   * 
     160   * \return The specified port 
     161   */ 
     162  inline core::tPortWrapperBase &GetPort(size_t index) 
     163  { 
     164    assert(index < this->NumberOfPorts()); 
     165    return *this->port_wrappers[index]; 
     166  } 
     167 
     168  /*! Access a specific port's changed flag 
     169   * 
     170   * \param index   The index of the port in this pack 
     171   * 
     172   * \return Whether the port has changed or not 
     173   */ 
     174  inline bool HasChanged(size_t index) 
     175  { 
     176    std::vector<bool> changed_flags = this->GetChangedFlags(tIndex()); // TODO: std::vector<bool> may be replaced by auto in c++14 and gcc < 4.9 to avoid heap allocation 
     177    return changed_flags[index]; 
     178  } 
     179 
     180  /*! Check if any port in the pack has changed 
     181   * 
     182   * \return Whether any port in the pack has changed 
     183   */ 
     184  inline bool Changed() 
     185  { 
     186    std::vector<bool> changed_flags = this->GetChangedFlags(tIndex()); // TODO: std::vector<bool> may be replaced by auto in c++14 and gcc < 4.9 to avoid heap allocation 
     187    return std::any_of(changed_flags.begin(), changed_flags.end(), [](bool x) 
     188    { 
     189      return x; 
     190    }); 
     191  } 
     192 
     193  /*! Publish a tuple of value through this port pack (only for output ports) 
     194   * 
     195   * \param values      A tuple of values that can be published via this portspacks ports 
     196   * \param timestamp   An optional timestamp to use (default: cNO_TIME) 
     197   */ 
     198  template <typename ... TValues> 
     199  inline void Publish(const std::tuple<TValues...> &values, const rrlib::time::tTimestamp timestamp = rrlib::time::cNO_TIME) 
     200  { 
     201    this->DispatchPublisher(tIndex(), values, timestamp); 
     202  } 
     203 
     204  /*! Deletion of the port pack's ports 
     205   */ 
     206  inline void ManagedDelete() 
     207  { 
     208    this->ManagedDelete(tIndex()); 
     209  } 
     210 
     211//---------------------------------------------------------------------- 
     212// Protected methods 
     213//---------------------------------------------------------------------- 
     214protected: 
     215 
     216  /*! Ctor with common port name prefix and port number offset to support legacy code 
     217   * 
     218   * The port names will follow the scheme "<prefix> [<offset>..]" 
     219   * 
     220   * \param parent        The parent framework element 
     221   * \param name_prefix   The common prefix used for all port names 
     222   * \param offset        Offset for numbering the created ports 
     223   */ 
     224  inline tPortPack(core::tFrameworkElement *parent, const std::string &name_prefix, size_t offset) 
     225  { 
     226    this->DispatchInitializer(parent, tIndex(), [&name_prefix, offset](size_t i) 
     227    { 
     228      return name_prefix + std::to_string(i + offset); 
     229    }); 
     230  } 
     231 
     232//---------------------------------------------------------------------- 
     233// Private fields and methods 
     234//---------------------------------------------------------------------- 
     235private: 
     236 
     237  tPorts ports; 
     238  core::tPortWrapperBase *port_wrappers[sizeof...(TTypes)]; 
     239 
     240  template <size_t Tindex> 
     241  int InitializePort(core::tFrameworkElement *parent, const std::string &name) 
     242  { 
     243    std::get<Tindex>(this->ports).reset(new typename std::tuple_element<Tindex, std::tuple<TPort<TTypes>...>>::type(name, parent)); 
     244    std::get<Tindex>(this->ports)->Init(); 
     245    return 0; 
     246  } 
     247 
     248  template <int ... Tindex, typename TNameGenerator> 
     249  void DispatchInitializer(core::tFrameworkElement *parent, rrlib::util::tIntegerSequence<Tindex...>, TNameGenerator name_generator) 
     250  { 
     251    __attribute__((unused)) int foo[] = { InitializePort<Tindex>(parent, name_generator(Tindex))... }; 
     252    auto port_wrappers = std::initializer_list<core::tPortWrapperBase *> {std::get<Tindex>(this->ports).get()...}; 
     253    std::copy(port_wrappers.begin(), port_wrappers.end(), this->port_wrappers); 
     254  } 
     255 
     256  template <typename T, typename TValue> 
     257  int PublishPort(T &port, const TValue &value, const rrlib::time::tTimestamp &timestamp) 
     258  { 
     259    port.Publish(value, timestamp); 
     260    return 0; 
     261  } 
     262  template <int ... Tindex, typename ... TValues> 
     263  void DispatchPublisher(rrlib::util::tIntegerSequence<Tindex...>, const std::tuple<TValues...> &values, const rrlib::time::tTimestamp timestamp) 
     264  { 
     265    __attribute__((unused)) int foo[] = { PublishPort(*std::get<Tindex>(this->ports), std::get<Tindex>(values), timestamp)... }; 
     266  } 
     267 
     268  template <int ... Tindex> 
     269  inline std::initializer_list<bool> GetChangedFlags(rrlib::util::tIntegerSequence<Tindex...>) 
     270  { 
     271    return { std::get<Tindex>(this->ports)->HasChanged()..., false }; 
     272  } 
     273 
     274  template <int ... Tindex> 
     275  inline void ManagedDelete(rrlib::util::tIntegerSequence<Tindex...>) 
     276  { 
     277    for (auto p : this->port_wrappers) 
     278    { 
     279      p->GetWrapped()->ManagedDelete(); 
     280    } 
     281  } 
     282 
     283}; 
     284 
    65285/*! 
    66286 * This class creates a list of instances of the given port template 
     
    69289 * port on a specific layer at runtime. 
    70290 * 
     291 * \note This class exists to support legacy code that used the initial 
     292 *       version with tTypeList and 1-based port numbers. 
     293 * 
    71294 * \param TPort       A port class template to use for every packed port 
    72295 * \param TTypeList   A list of the data types used in the ports. e.g. rrlib::util::tTypeList 
    73  * \param Tsize       The pack creates ports using TTypeList[0] to TTypeList[Tsize - 1]. This parameter must not be greater than TTypeList::cSIZE - 1 and is typically inferred and not set by the user. 
    74296 */ 
    75 template <template <typename> class TPort, typename TTypeList, size_t Tsize = rrlib::util::type_list::tSizeOf<TTypeList>::cVALUE> 
    76 class tPortPack : private tPortPack < TPort, TTypeList, Tsize - 1 > 
     297template <template <typename> class TPort, typename ... TTypes> 
     298class tPortPack<TPort, rrlib::util::tTypeList<TTypes...>> : public tPortPack<TPort, TTypes...> 
    77299{ 
    78  
    79   template <bool value, typename> 
    80   using CheckIterators = std::integral_constant<bool, value>; 
    81  
    82 //---------------------------------------------------------------------- 
    83 // Public methods and typedefs 
    84 //---------------------------------------------------------------------- 
    85300public: 
    86  
     301  using tPortPack<TPort, TTypes...>::tPortPack; 
    87302  inline tPortPack(core::tFrameworkElement *parent, const std::string &name_prefix) : 
    88     tPortPack < TPort, TTypeList, Tsize - 1 > (parent, name_prefix), 
    89     port(name_prefix + std::to_string(Tsize), parent) 
    90   { 
    91     this->port.Init(); 
    92   } 
    93  
    94   template <typename TIterator, typename std::enable_if<CheckIterators<(Tsize> 1), TIterator>::value, int>::type = 0 > 
    95       inline tPortPack(core::tFrameworkElement *parent, TIterator names_begin, TIterator names_end) : 
    96         tPortPack < TPort, TTypeList, Tsize - 1 > (parent, names_begin, names_end - 1), 
    97         port(*(names_end - 1), parent) 
    98   { 
    99     this->port.Init(); 
    100   } 
    101  
    102   template <typename TIterator, typename std::enable_if<CheckIterators<(Tsize == 1), TIterator>::value, int>::type = 0> 
    103   inline tPortPack(core::tFrameworkElement *parent, TIterator names_begin, TIterator names_end) : 
    104     tPortPack < TPort, TTypeList, Tsize - 1 > (parent, *names_begin), 
    105     port(*names_begin, parent) 
    106   { 
    107     this->port.Init(); 
    108   } 
    109  
    110   inline size_t NumberOfPorts() const 
    111   { 
    112     return Tsize; 
    113   } 
    114  
    115   inline core::tPortWrapperBase &GetPort(size_t index) 
    116   { 
    117     assert(index < this->NumberOfPorts()); 
    118     if (index == Tsize - 1) 
    119     { 
    120       return this->port; 
    121     } 
    122     return tPortPack < TPort, TTypeList, Tsize - 1 >::GetPort(index); 
    123   } 
    124  
    125   inline bool HasChanged(size_t index) 
    126   { 
    127     assert(index < this->NumberOfPorts()); 
    128     if (index == Tsize - 1) 
    129     { 
    130       return this->port.HasChanged(); 
    131     } 
    132     return tPortPack < TPort, TTypeList, Tsize - 1 >::HasChanged(index); 
    133   } 
    134  
    135   inline void ManagedDelete() 
    136   { 
    137     this->port.GetWrapped()->ManagedDelete(); 
    138     tPortPack < TPort, TTypeList, Tsize - 1 >::ManagedDelete(); 
    139   } 
    140  
    141 //---------------------------------------------------------------------- 
    142 // Private fields and methods 
    143 //---------------------------------------------------------------------- 
    144  
    145 private: 
    146  
    147   TPort < typename TTypeList::template tAt < Tsize - 1 >::tResult > port; 
    148  
    149 }; 
    150  
    151 //! The partial specialization of tPortPack to terminate recursion 
    152 template <template <typename> class TPort, typename TTypeList> 
    153 struct tPortPack <TPort, TTypeList, 0> 
    154 { 
    155   inline tPortPack(core::tFrameworkElement *parent, const std::string &name_prefix) 
    156   {} 
    157  
    158   template <typename TIterator> 
    159   inline tPortPack(core::tFrameworkElement *parent, TIterator names_begin, TIterator names_end) 
    160   {} 
    161  
    162   inline core::tPortWrapperBase &GetPort(size_t index) 
    163   { 
    164     return *reinterpret_cast<core::tPortWrapperBase *>(0); 
    165   }; 
    166  
    167   inline bool HasChanged(size_t index) 
    168   { 
    169     return false; 
    170   }; 
    171  
    172   inline void ManagedDelete() 
     303    tPortPack<TPort, TTypes...>(parent, name_prefix, 1) 
    173304  {} 
    174305}; 
  • tests/test_collection.cpp

    r125 r126  
    4545#include "plugins/data_ports/tThreadLocalBufferManagement.h" 
    4646#include "plugins/data_ports/tPortPack.h" 
     47 
     48#include "plugins/structure/tModule.h" 
    4749 
    4850//---------------------------------------------------------------------- 
     
    369371} 
    370372 
     373struct mTestModule : structure::tModule 
     374{ 
     375  using tModule::tModule; 
     376  void Update() override {} 
     377}; 
     378 
     379template <typename ... T, typename NAMES> 
     380void PortPackTestHelper(core::tFrameworkElement *parent, const std::initializer_list<NAMES> &names_initializer) 
     381{ 
     382  data_ports::tPortPack<mTestModule::tInput, T...> ports(parent, "X"); 
     383 
     384  RRLIB_UNIT_TESTS_EQUALITY(sizeof...(T), ports.NumberOfPorts()); 
     385  for (size_t i = 0; i < sizeof...(T); ++i) 
     386  { 
     387    RRLIB_UNIT_TESTS_EQUALITY("X" + std::to_string(i), ports.GetPort(i).GetName()); 
     388  } 
     389 
     390  std::array<NAMES, sizeof...(T)> names; 
     391  assert(names_initializer.size() == names.size()); 
     392  std::copy(names_initializer.begin(), names_initializer.end(), names.begin()); 
     393  data_ports::tPortPack<tInputPort, T...> named_ports(parent, names.begin(), names.end()); 
     394 
     395  RRLIB_UNIT_TESTS_EQUALITY(sizeof...(T), named_ports.NumberOfPorts()); 
     396  for (size_t i = 0; i < sizeof...(T); ++i) 
     397  { 
     398    RRLIB_UNIT_TESTS_EQUALITY(std::string(names[i]), named_ports.GetPort(i).GetName()); 
     399  } 
     400} 
     401 
    371402class DataPortsTestCollection : public rrlib::util::tUnitTestSuite 
    372403{ 
     
    374405  RRLIB_UNIT_TESTS_ADD_TEST(Test); 
    375406  RRLIB_UNIT_TESTS_ADD_TEST(PortPack); 
     407  RRLIB_UNIT_TESTS_ADD_TEST(PortPackLegacy); 
    376408  RRLIB_UNIT_TESTS_END_SUITE; 
    377409 
     
    403435  void PortPack() 
    404436  { 
    405     auto parent = new core::tFrameworkElement(&core::tRuntimeEnvironment::GetInstance(), "TestPortPack"); 
     437    auto parent = new mTestModule(&core::tRuntimeEnvironment::GetInstance(), "TestModule"); 
     438 
     439    PortPackTestHelper<int, double, std::string, bool>(parent, {"foo", "bar", "baz", "fnord"}); 
     440 
     441    std::array<std::string, 0> empty_names; 
     442    RRLIB_UNIT_TESTS_EQUALITY(size_t(0), data_ports::tPortPack<mTestModule::tInput>(parent, "X").NumberOfPorts()); 
     443    RRLIB_UNIT_TESTS_EQUALITY(size_t(0), data_ports::tPortPack<mTestModule::tInput>(parent, empty_names.begin(), empty_names.end()).NumberOfPorts()); 
     444  } 
     445 
     446  void PortPackLegacy() 
     447  { 
     448    auto parent = new mTestModule(&core::tRuntimeEnvironment::GetInstance(), "TestModule"); 
    406449 
    407450    using tTypeList = rrlib::util::tTypeList<int, double, std::string, bool>; 
    408     data_ports::tPortPack<tInputPort, tTypeList> ports(parent, "X"); 
     451    data_ports::tPortPack<mTestModule::tInput, tTypeList> ports(parent, "X"); 
    409452 
    410453    RRLIB_UNIT_TESTS_EQUALITY(tTypeList::cSIZE, ports.NumberOfPorts()); 
     
    416459 
    417460    std::array<std::string, tTypeList::cSIZE> names {"foo", "bar", "baz", "fnord"}; 
    418     data_ports::tPortPack<tInputPort, tTypeList> named_ports(parent, names.begin(), names.end()); 
     461    data_ports::tPortPack<mTestModule::tInput, tTypeList> named_ports(parent, names.begin(), names.end()); 
    419462 
    420463    for (size_t i = 0; i < tTypeList::cSIZE; ++i) 
Note: See TracChangeset for help on using the changeset viewer.