13d2ff81e3f34c2a5f709ccbff049914306f4748
[senf.git] / senf / PPI / MultiConnectorMixin.hh
1 // $Id$
2 //
3 // Copyright (C) 2009
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief MultiConnectorMixin public header */
25
26 #ifndef HH_SENF_PPI_MultiConnectorMixin_
27 #define HH_SENF_PPI_MultiConnectorMixin_ 1
28
29 // Custom includes
30 #include <senf/config.hh>
31 #include <boost/ptr_container/ptr_map.hpp>
32 #include <boost/ptr_container/ptr_vector.hpp>
33 #include <boost/mpl/if.hpp>
34 #include "Connectors.hh"
35 #include "Setup.hh"
36
37 #ifndef SENF_MULTI_CONNECTOR_MAX_ARGS
38 #define SENF_MULTI_CONNECTOR_MAX_ARGS 3
39 #endif
40
41 #include "MultiConnectorMixin.mpp"
42 #include "MultiConnectorMixin.ih"
43 ///////////////////////////////hh.p////////////////////////////////////////
44
45 namespace senf {
46 namespace ppi {
47
48 #ifdef DOXYGEN
49
50     // For exposition only.
51     // Other implementations with 0..SENF_MULTI_CONNECTOR_MAX_ARGS arguments accordingly
52     // The real implementation includes a boost::enable_if condition in the return value, which is
53     // used to only enable the correct overload: The first overload, if the MultiConnector is an
54     // output, otherwise the second overload.
55
56     /** \brief Connect MultiConnector source to arbitrary target
57
58         Additional implementations with 0..SENF_MULTI_CONNECTOR_MAX_ARGS arguments.
59
60         \related module::MultiConnectorMixin
61      */
62     template <class MultiConnectorSource, class Target, class A1>
63     MultiConnectorSource::ConnectorType & connect(
64         MultiConnectorSource & source, A1 const & a1, Target & target);
65
66     /** \brief Connect arbitrary source to MultiConnector target
67
68         Additional implementations with 0..SENF_MULTI_CONNECTOR_MAX_ARGS arguments.
69
70         \related module::MultiConnectorMixin
71      */
72     template <class Source, class MultiConnectorTarget, class A1>
73     MultiConnectorTarget::ConnectorType & connect(
74         Source & source, MultiConnectorTarget & target, A1 const & a1);
75
76     template <class MultiConnectorSource, class MultiConnectorTarget, class A1, class A2>
77     std::pair<MultiConnectorSource::ConnectorType &, MultiConnectorTarget::ConnectorType &>
78     connect(
79         MultiConnectorSource & source, MultiConnectorTarget & target, A1 const & a1, A2 const & a2);
80
81 #else
82
83     // Include 'senf::ppi namespace member declarations' from MultiConnectorMixin.mpp
84 #   define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
85             0, \
86             SENF_MULTI_CONNECTOR_MAX_ARGS, \
87             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
88             2 ))
89 #   include BOOST_PP_ITERATE()
90
91 #endif
92
93 namespace module {
94
95     namespace detail { class MultiConnectorMixinAccess; }
96
97     /** \brief Multi-Connector management
98
99         This mixin provides a module with support for a runtime configurable number of input or
100         output connectors.
101         \code
102         class MyModule
103             : public senf::ppi::module::Module,
104               public senf::ppi::module::MultiConnectorMixin<
105                   MyModule, senf::ppi::connector::ActiveInput<> >
106         {
107             SENF_PPI_MODULE(MyModule);
108         public:
109             senf::ppi::connector::PassiveOutput<> output;
110
111             // ...
112
113         private:
114             void connectorSetup(senf::ppi::connector::ActiveInput & input)
115             {
116                 route(input, output);
117                 input.onThrottle(&MyModule::doThrottle);
118             }
119
120             // Optional
121             void connectorDestroy(senf::ppi::connector::ActiveInput const & input)
122             {
123                  //  whatever
124             }
125
126             void doThrottle()
127             {
128                 // ...
129             }
130
131             friend class senf::ppi::module::MultiConnectorMixin<
132                 MyModule, senf::ppi::connector::ActiveInput<> >;
133         }
134         \endcode
135
136         Using the MultiConnectorMixin consists of
137         \li inheriting from MultiConnectorMixin
138         \li defining a function \c connectorSetup
139         \li declaring the mixin as a friend
140         \li optionally defining \c connectorDestroy to be notified when connectors are disconnected
141
142         The MultiConnectorMixin takes several template arguments
143         \li the name of the derived class, \a Self_
144         \li the type of connector, \a ConnectorType_
145         \li an optional \a KeyType_ if a mapping container is required
146         \li an optional \a ContainerType_ to replace the default \c boost::ptr_vector or \c
147             boost::ptr_map.
148
149         \section senf_ppi_multiconnector_sequence Sequence/vector based multi connector mixin
150
151         If no \a KeyType_ is given (or the \a KeyType_ is \c void), the mixin will use a sequence
152         container, by default a \c boost::ptr_vector to store the connectors. In this case, new
153         connectors will be added to the end of the container. The \c connectorSetup() routine
154         however may be used to move the inserted connector to some other location, e.g.
155         \code
156         container().insert(begin(), container().pop_back().release());
157         \endcode
158         which will move the new connector from the end to the beginning. If you want to abort adding
159         the new connector, you may throw an exception.
160
161         \par "Example:" senf::ppi::module::PriorityJoin
162
163         \section senf_ppi_multiconnector_map Map based multi connector mixin
164
165         If \a KeyType_ is given (and is not \c void), the mixin will use a mapping container, by
166         default a \c boost::ptr_map to store the connectors. The \c connectorSetup() member function
167         must return the key value under which the new connector will be stored. The new connector
168         will this be written to the container only \e after \c connectorSetup() returns.
169
170         When the returned key is not unique, the new connector will \e replace the old one. If this
171         is not what you want, either check for an already existing member and throw an exception in
172         \c connectorSetup() or replace the \c boost::ptr_map by a \c boost::ptr_multimap using the
173         fourth template argument to MultiConnectorMixin.
174
175         \par "Example:" senf::ppi::module::AnnotationRouter
176
177         \section senf_ppi_multiconnector_connect Connect and additional connectorSetup() arguments
178
179         When connecting to a module using the MultiConnectorMixin, every new connect call will
180         allocate a new connector
181         \code
182         MyModule muModule;
183
184         senf::ppi::connect(someModule, myModule);
185         \endcode
186         Some modules will expect additional arguments to be passed (see below)
187         \code
188         MyModule myModule;
189
190         senf::ppi::connect(mod1, myModule);     // index = 0, see below
191         senf::ppi::connect(mod2, myModule, 1);  // index = 1, see below
192         \endcode
193         To declare these additional arguments, just declare \c connectorSetup() with those
194         additional arguments:
195         \code
196         void connectorSetup(MyModule::ConnectorType & input, int index=0)
197         {
198             container().insert(container().begin()+index, container().pop_back().release());
199         }
200         \endcode
201
202         \par "Advanced note:" These additional arguments are always passed by const-reference. If
203             you need to pass a non-const reference, declare the \c connectorSetup() argument as
204             non-const reference and wrap the real argument using \c boost::ref() (The reason for
205             this is known as 'The forwarding problem').
206
207         \section senf_ppi_multiconnector_advanced Advanced usage: Managing your own container
208
209         If you need to use a completely different type of container, you can take over the container
210         management yourself. To do this, pass \c void as container type and change \c
211         connectorSetup() to take an \c std::auto_ptr as argument. \c connectorSetup() must ensure to
212         save this connector in some container or throw an exception.
213
214         Implementing \c connectorDestroy now is \e mandatory. The signature is changed to take a
215         pointer as argument
216         \code
217         class MyModule
218             : public senf::ppi::module::Module,
219               public senf::ppi::module::MultiConnectorMixin<
220                   MyModule, senf::ppi::connector::ActiveInput<>, void, void >
221         {
222             SENF_PPI_MODULE(MyModule);
223         public:
224             // ...
225
226         private:
227             void connectorSetup(std::auto_ptr<ConnectorType> conn, unsigned p)
228             {
229                 if (p>connectors_.size())
230                    throw SomeErrorException();
231                 route(*conn, output);
232                 connectors_.insert(connectors_.begin()+p,conn);
233             }
234
235             void connectorDestroy(ConnectorType const * conn)
236             {
237                 using boost::lambda::_1;
238                 boost::ptr_vector<ConnectorType>::iterator i (
239                     std::find_if(connectors_.begin(),connectors_.end(), &_1==conn))
240                 if (i != connectors_.end())
241                     connectors_.erase(i);
242             }
243
244             boost::ptr_vector<ConnectorType> connectors_;
245         };
246         \endcode
247         \warning You must make absolutely sure the connector does not get deleted when returning
248             normally from \c connectorSetup(): The connector \e must be saved somewhere
249             successfully, otherwise your code will break.
250
251      */
252     template <class Self_,
253               class ConnectorType_,
254               class KeyType_=void,
255               class ContainerType_=typename detail::MultiConnectorDefaultContainer<
256                                                KeyType_,ConnectorType_>::type>
257     class MultiConnectorMixin
258         : private detail::MultiConnectorSelectBase<ConnectorType_>::type
259     {
260     public:
261         typedef ConnectorType_ ConnectorType; ///< Type of MultiConnector connector
262
263     protected:
264         typedef ContainerType_ ContainerType; ///< Type of connector container
265
266         ContainerType_ & connectors();        ///< Get connector container
267         ContainerType_ const & connectors() const; ///< Get connectors container (const)
268
269         void connectorDestroy(ConnectorType const &);
270
271     private:
272
273 #ifndef DOXYGEN
274
275         // Include 'MultiConnectorMixin member declaration' from MultiConnectorMixin.mpp
276 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
277             0, \
278             SENF_MULTI_CONNECTOR_MAX_ARGS, \
279             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
280             1 ))
281 #       include BOOST_PP_ITERATE()
282
283 #endif
284
285         void disconnected(ConnectorType_ const & c);
286
287         friend class detail::MultiConnectorMixinAccess;
288         friend class detail::MultiConnectorWrapper<Self_,ConnectorType_>;
289
290         ContainerType_ connectors_;
291     };
292
293 #ifndef DOXYGEN
294
295     template <class Self_,
296               class ConnectorType_,
297               class ContainerType_>
298     class MultiConnectorMixin<Self_,ConnectorType_,void,ContainerType_>
299         : private detail::MultiConnectorSelectBase<ConnectorType_>::type
300     {
301     public:
302         typedef ConnectorType_ ConnectorType;
303
304     protected:
305         typedef ContainerType_ ContainerType;
306
307         ContainerType_ & connectors();
308         ContainerType_ const & connectors() const;
309
310         void connectorDestroy(ConnectorType const &);
311
312     private:
313
314         // Include 'MultiConnectorMixin member declaration' from MultiConnectorMixin.mpp
315 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
316             0, \
317             SENF_MULTI_CONNECTOR_MAX_ARGS, \
318             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
319             1 ))
320 #       include BOOST_PP_ITERATE()
321
322         void disconnected(ConnectorType_ const & c);
323
324         friend class detail::MultiConnectorMixinAccess;
325         friend class detail::MultiConnectorWrapper<Self_,ConnectorType_>;
326
327         ContainerType_ connectors_;
328     };
329
330     template <class Self_,
331               class ConnectorType_>
332     class MultiConnectorMixin<Self_,ConnectorType_,void,void>
333         : private detail::MultiConnectorSelectBase<ConnectorType_>::type
334     {
335     public:
336         typedef ConnectorType_ ConnectorType;
337
338     private:
339
340         // Include 'MultiConnectorMixin member declaration' from MultiConnectorMixin.mpp
341 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
342             0, \
343             SENF_MULTI_CONNECTOR_MAX_ARGS, \
344             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
345             1 ))
346 #       include BOOST_PP_ITERATE()
347
348         void disconnected(ConnectorType_ const & c);
349
350         friend class detail::MultiConnectorMixinAccess;
351         friend class detail::MultiConnectorWrapper<Self_,ConnectorType_>;
352     };
353
354 #endif
355
356 }}}
357
358 ///////////////////////////////hh.e////////////////////////////////////////
359 //#include "MultiConnectorMixin.cci"
360 #include "MultiConnectorMixin.ct"
361 #include "MultiConnectorMixin.cti"
362 #endif
363
364 \f
365 // Local Variables:
366 // mode: c++
367 // fill-column: 100
368 // comment-column: 40
369 // c-file-style: "senf"
370 // indent-tabs-mode: nil
371 // ispell-local-dictionary: "american"
372 // compile-command: "scons -u test"
373 // End: