PPI: Implement a more sane multi-connector connect API
[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, Target & target, A1 const & a1);
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::Modulem,
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             void doThrottle()
121             { 
122                 // ...
123             }
124
125             friend class senf::ppi::module::MultiConnectorMixin<
126                 MyModule, senf::ppi::connector::ActiveInput<> >
127         }
128         \endcode
129
130         Using the MultiConnectorMixin consists of
131         \li inheriting from MultiConnectorMixin
132         \li defining a function \c connectorSetup
133         \li declaring the mixin as a friend
134
135         The MultiConnectorMixin takes several template arguments
136         \li the name of the derived class, \a Self_
137         \li the type of connector, \a ConnectorType_
138         \li an optional \a KeyType_ if a mapping container is required
139         \li an optional \a ContainerType_ to replace the default \c boost::ptr_vector or \c
140             boost::ptr_map.
141
142         \section senf_ppi_multiconnector_sequence Sequence/vector based multi connector mixin
143
144         If no \a KeyType_ is given (or the \a KeyType_ is \c void), the mixin will use a sequence
145         container, by default a \c boost::ptr_vector to store the connectors. In this case, new
146         connectors will be added to the end of the container. The \c connectorSetup() routine
147         however may be used to move the inserted connector to some other location, e.g.
148         \code
149         container().insert(begin(), container().pop_back().release());
150         \endcode
151         which will move the new connector from the end to the beginning. 
152         \warning If you throw an exception from \c connectorSetup(), you must do so \e before moving
153             the new connector around since the mixin will remove the last element from the container
154             on an exception.
155
156         \par "Example:" senf::ppi::module::PriorityJoin
157
158         \section senf_ppi_multiconnector_map Map based multi connector mixin
159
160         If \a KeyType_ is given (and is not \c void), the mixin will use a mapping container, by
161         default a \c boost::ptr_map to store the connectors. The \c connectorSetup() member function
162         must return the key value under which the new connector will be stored. The new connector
163         will this be written to the container only \e after \c connectorSetup() returns.
164
165         When the returned key is not unique, the new connector will \e replace the old one. If this
166         is not, what you want, either check for an already existing member and throw an exception in
167         \c connectorSetup() or replace the \c boost::ptr_map by a \c boost::ptr_multimap using the
168         fourth template argument to MultiConnectorMixin.
169
170         \par "Example:" senf::ppi::module::AnnotationRouter
171
172         \section senf_ppi_multiconnector_connect Connect and additional connectorSetup() arguments
173
174         When connecting to a module using the MultiConnectorMixin, every new connect call will
175         allocate a new connector
176         \code
177         MyModule muModule;
178         
179         senf::ppi::connect(someModule, myModule);
180         \endcode
181         Some modules will expect additional arguments to be passed (see below)
182         \code
183         MyModule myModule;
184
185         senf::ppi::connect(mod1, myModule);     // index = 0, see below
186         senf::ppi::connect(mod2, myModule, 1);  // index = 1, see below
187         \endcode
188         To declare these additional arguments, just declare \c connectorSetup() with those
189         additional arguments:
190         \code
191         void connectorSetup(MyModule::ConnectorType & input, int index=0)
192         {
193             container().insert(container().begin()+index, container().pop_back().release());
194         }
195         \endcode
196
197         \par "Advanced note:" These additional arguments are always passed by const-reference. If
198             you need to pass a non-const reference, declare the \c connectorSetup() argument as
199             non-const reference and wrap the real argument using \c boost::ref() (The reason for
200             this is known as 'The forwarding problem'
201
202         \section senf_ppi_multiconnector_advanced Advanced usage: Managing your own container
203
204         If you need to use a completely different type of container, you can take over the container
205         management yourself. To do this, pass \c void as container type and change \c
206         connectorSetup() to take an \c std::auto_ptr as argument. \c connectorSetup() must ensure to
207         save this connector in some container or throw an exception
208         \code
209         class MyModule 
210             : public senf::ppi::module::Module,
211               public senf::ppi::module::MultiConnectorMixin<
212                   MyModule, senf::ppi::connector::ActiveInput<>, void, void >
213         {
214             SENF_PPI_MODULE(MyModule);
215         public:
216             // ...
217
218         private:
219             void connectorSetup(std::auto_ptr<ConnectorType> conn, unsigned p)
220             {
221                 if (p>connectors_.size())
222                    throw SomeErrorException();
223                 route(*conn, output);
224                 connectors_.insert(connectors_.begin()+p,conn);
225             }
226
227             boost::ptr_vector<ConnectorType> connectors_;
228         };
229         \endcode
230         \warning You must make absolutely sure the connector does not get deleted when returning
231             normally from \c connectorSetup(): The connector \e must be saved somewhere
232             successfully, otherwise your code will break.
233
234      */
235     template <class Self_, 
236               class ConnectorType_, 
237               class KeyType_=void, 
238               class ContainerType_=typename detail::MultiConnectorDefaultContainer<
239                                                KeyType_,ConnectorType_>::type>
240     class MultiConnectorMixin 
241         : private detail::MultiConnectorSelectBase<ConnectorType_>::type
242     {
243     public:
244         typedef ConnectorType_ ConnectorType; ///< Type of MultiConnector connector
245
246     protected:
247         typedef ContainerType_ ContainerType; ///< Type of connector container
248         ContainerType_ & connectors();        ///< Get connector container
249         ContainerType_ const & connectors() const; ///< Get connectors container (const)
250
251     private:
252 #if 0
253         // For exposition only
254         // Other implementations with 0..SENF_MULTI_CONNECTOR_MAX_ARGS arguments accordingly
255
256         tempalte <class A1>
257         ConnectorType_ & newConnector(A1 const & a1);
258
259 #endif
260 #ifndef DOXYGEN
261
262         // Include 'MultiConnectorMixin member declaration' from MultiConnectorMixin.mpp
263 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
264             0, \
265             SENF_MULTI_CONNECTOR_MAX_ARGS, \
266             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
267             1 ))
268 #       include BOOST_PP_ITERATE()
269
270 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
271             0, \
272             2*SENF_MULTI_CONNECTOR_MAX_ARGS, \
273             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
274             9 ))
275 #       include BOOST_PP_ITERATE()
276
277 #endif
278
279         friend class detail::MultiConnectorMixinAccess;
280         
281         ContainerType_ connectors_;
282     };
283
284 #ifndef DOXYGEN
285
286     template <class Self_,
287               class ConnectorType_,
288               class ContainerType_>
289     class MultiConnectorMixin<Self_,ConnectorType_,void,ContainerType_>
290         : private detail::MultiConnectorSelectBase<ConnectorType_>::type
291     {
292     public:
293         typedef ConnectorType_ ConnectorType;
294         
295     protected:
296         typedef ContainerType_ ContainerType;
297         ContainerType_ & connectors();
298
299     private:
300
301 #if 0
302         // For exposition only
303         // Other implementations with 0..SENF_MULTI_CONNECTOR_MAX_ARGS arguments accordingly
304
305         tempalte <class A1>
306         ConnectorType_ & newConnector(A1 const & a1);
307
308         // See above for an additional note regarding the boost::enable_if in the real
309         // implementation
310         
311         template <class Source, class Target, class A1>
312         friend Source::ConnectorType & senf::ppi::connect(Source & source, 
313                                                           Target & target, 
314                                                           A1 const & a1);
315
316         template <class Source, class Target, class A1>
317         friend Target::ConnectorType & senf::ppi::connect(Source & source,
318                                                           Target & target,
319                                                           A1 const & a1);
320 #endif
321
322         // Include 'MultiConnectorMixin member declaration' from MultiConnectorMixin.mpp
323 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
324             0, \
325             SENF_MULTI_CONNECTOR_MAX_ARGS, \
326             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
327             1 ))
328 #       include BOOST_PP_ITERATE()
329         
330 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
331             0, \
332             2*SENF_MULTI_CONNECTOR_MAX_ARGS, \
333             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
334             9 ))
335 #       include BOOST_PP_ITERATE()
336
337         friend class detail::MultiConnectorMixinAccess;
338
339         ContainerType_ connectors_;
340     };
341
342     template <class Self_, 
343               class ConnectorType_>
344     class MultiConnectorMixin<Self_,ConnectorType_,void,void>
345         : private detail::MultiConnectorSelectBase<ConnectorType_>::type
346     {
347     public:
348         typedef ConnectorType_ ConnectorType;
349
350     private:
351
352 #if 0
353         // For exposition only
354         // Other implementations with 0..SENF_MULTI_CONNECTOR_MAX_ARGS arguments accordingly
355
356         tempalte <class A1>
357         ConnectorType_ & newConnector(A1 const & a1);
358
359         // See above for an additional note regarding the boost::enable_if in the real
360         // implementation
361         
362         template <class Source, class Target, class A1>
363         friend Source::ConnectorType & senf::ppi::connect(Source & source, 
364                                                           Target & target, 
365                                                           A1 const & a1);
366
367         template <class Source, class Target, class A1>
368         friend Target::ConnectorType & senf::ppi::connect(Source & source,
369                                                           Target & target,
370                                                           A1 const & a1);
371 #endif
372
373         // Include 'MultiConnectorMixin member declaration' from MultiConnectorMixin.mpp
374 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
375             0, \
376             SENF_MULTI_CONNECTOR_MAX_ARGS, \
377             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
378             1 ))
379 #       include BOOST_PP_ITERATE()
380
381 #       define BOOST_PP_ITERATION_PARAMS_1 (4, ( \
382             0, \
383             2*SENF_MULTI_CONNECTOR_MAX_ARGS, \
384             SENF_ABSOLUTE_INCLUDE_PATH(PPI/MultiConnectorMixin.mpp), \
385             9 ))
386 #       include BOOST_PP_ITERATE()
387
388         friend class detail::MultiConnectorMixinAccess;
389     };
390
391 #endif
392         
393 }}}
394
395 ///////////////////////////////hh.e////////////////////////////////////////
396 //#include "MultiConnectorMixin.cci"
397 //#include "MultiConnectorMixin.ct"
398 #include "MultiConnectorMixin.cti"
399 #endif
400
401 \f
402 // Local Variables:
403 // mode: c++
404 // fill-column: 100
405 // comment-column: 40
406 // c-file-style: "senf"
407 // indent-tabs-mode: nil
408 // ispell-local-dictionary: "american"
409 // compile-command: "scons -u test"
410 // End: