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