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