libt2n: (gerd) bugfixes, better logging, unit tests for wrapper, ignore handler still...
[libt2n] / src / client_wrapper.hxx
1 /***************************************************************************
2  *   Copyright (C) 2008 by Gerd v. Egidy and Reinhard Pfau                 *
3  *   gve@intra2net.com                                                     *
4  *                                                                         *
5  *   This library is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU Lesser General Public License version   *
7  *   2.1 as published by the Free Software Foundation.                     *
8  *                                                                         *
9  *   This library is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU Lesser General Public License for more details.                   *
13  *                                                                         *
14  *   You should have received a copy of the GNU Lesser General Public      *
15  *   License along with this program; if not, write to the                 *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 #ifndef __LIBT2N_CLIENT_WRAPPER
20 #define __LIBT2N_CLIENT_WRAPPER
21
22 #include <functional>
23
24 #include <boost/config.hpp>
25 #include <boost/bind.hpp>
26 #include <boost/function.hpp>
27 #include <boost/preprocessor.hpp>
28
29 #include <client.hxx>
30 #include <command_client.hxx>
31
32 #ifndef T2N_SINGLETON_WRAPPER_MAX_ARGS
33 #define T2N_SINGLETON_WRAPPER_MAX_ARGS 9
34 #endif
35
36 namespace libt2n
37 {
38
39 class ConnectionWrapper
40 {
41     private:
42         long long command_timeout_usec;
43         long long hello_timeout_usec;
44
45     protected:
46         log_level_values log_level;
47         std::ostream *logstream;
48         void set_logging_on_connection(client_connection& c);
49
50     public:
51         ConnectionWrapper()
52             : log_level(none), logstream(NULL), 
53               command_timeout_usec(command_client::command_timeout_usec_default),
54               hello_timeout_usec(command_client::hello_timeout_usec_default)
55             { }
56
57         virtual ~ConnectionWrapper()
58             { }
59
60         virtual client_connection* get_connection()=0;
61
62         virtual void handle(command_client* stubBase, boost::function< void() > f)
63         {
64             f();
65         }
66
67         long long get_command_timeout_usec(void)
68             { return command_timeout_usec; }
69
70         void set_command_timeout_usec(long long _command_timeout_usec)
71             { command_timeout_usec=_command_timeout_usec; }
72
73         long long get_hello_timeout_usec(void)
74             { return hello_timeout_usec; }
75
76         void set_hello_timeout_usec(long long _hello_timeout_usec)
77             { hello_timeout_usec=_hello_timeout_usec; }
78
79         void set_logging(std::ostream *_logstream, log_level_values _log_level);
80
81         std::ostream* get_logstream(log_level_values level);
82 };
83
84 // contains the internal stuff needed for T2nWrapper
85 namespace detail
86 {
87
88     template< typename T >
89     struct TypeWrap
90     {
91         typedef T type;
92     };
93
94     template< >
95     struct TypeWrap< void >
96     {
97         typedef int type;
98     };
99
100     template< typename R >
101     struct Call
102     {
103         typedef boost::function< R() > FuncType;
104
105         FuncType function;
106         R& result;
107
108         Call( FuncType f, R& res ) : function(f), result( res ) {}
109
110         void operator()()
111         {
112             result= function();
113         }
114     };
115
116     template< >
117     struct Call<void>
118     {
119         typedef boost::function< void() > FuncType;
120         typedef TypeWrap< void >::type ResultType;
121
122         FuncType function;
123         ResultType& result;
124
125         Call( FuncType f, ResultType& res ) : function(f), result( res ) {}
126
127         void operator()()
128         {
129             function();
130             result= ResultType();
131         }
132     };
133 } // eo namespace detail
134
135 class T2nSingletonWrapperMessages
136 {
137     protected:
138         static const char* NotInitializedMessage;
139 };
140
141 template< class Client >
142 class T2nSingletonWrapper : public T2nSingletonWrapperMessages
143 {
144     private:
145         std::auto_ptr<Client> Stub;
146
147         static std::auto_ptr<T2nSingletonWrapper> SingletonObject;
148         static std::auto_ptr<ConnectionWrapper> WrappedConnection;
149
150         // create a prep-method for each possible number of parameters
151 #define _GEN_ARG(z,n,d) Arg ## n arg ##n
152 #define _GEN_PREP(z,n,d) \
153         template< typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
154         static boost::function< R(Client*) > prep \
155         ( \
156             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
157             BOOST_PP_COMMA_IF(n) \
158             BOOST_PP_ENUM( n, _GEN_ARG, ~ ) \
159         ) \
160         { \
161             return boost::bind< R > \
162                 ( \
163                     f, _1  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,arg) \
164                 ); \
165         } // eo prep
166
167         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1) , _GEN_PREP, ~ )
168
169 #undef _GEN_PREP
170 #undef _GEN_ARG
171
172         T2nSingletonWrapper(std::auto_ptr<Client> stub)
173         {
174             Stub=stub;
175         }
176
177         static void init()
178         {
179             if (WrappedConnection.get() == NULL)
180                 throw std::logic_error(NotInitializedMessage);
181
182             std::auto_ptr<Client> stub(new Client(*(WrappedConnection->get_connection()),
183                 WrappedConnection->get_command_timeout_usec(),
184                 WrappedConnection->get_hello_timeout_usec()));
185
186             SingletonObject=std::auto_ptr<T2nSingletonWrapper>(new T2nSingletonWrapper(stub));
187         }
188
189         template< typename R >
190         static
191         typename detail::TypeWrap<R>::type real_exec( boost::function< R(Client*) > f)
192         {
193             ensure_singleton_there();
194
195             typename detail::TypeWrap<R>::type result;
196             detail::Call<R> call( boost::bind( f, SingletonObject->Stub.get()), result );
197             WrappedConnection->handle(SingletonObject->Stub.get(),call);
198             return result;
199         }
200
201     public:
202
203         static void set_connection(std::auto_ptr<ConnectionWrapper> wrappedConnection)
204         {
205             WrappedConnection=wrappedConnection;
206
207             // reset the singleton to NULL because the singleton must be constructed with current WrappedConnection
208             if (SingletonObject.get() != NULL)
209                 SingletonObject.reset();
210         }
211         static ConnectionWrapper* get_connection_wrapper(void)
212             { return WrappedConnection.get(); }
213
214         static void ensure_singleton_there(void)
215         {
216             if (SingletonObject.get() == NULL)
217                 init();
218         }
219
220         // create an exec-method for each possible number of parameters
221 #define _GEN_PLACEHOLDER(z,n,d) BOOST_PP_CAT(_,BOOST_PP_ADD(n,1))
222 #define _GEN_EXEC(z,n,d) \
223         template< typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
224         static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > exec \
225         ( \
226             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
227         ) \
228         { \
229             boost::function<R(Client*)>(*p)(R(Client::*)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
230                     BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg)) \
231                 = &T2nSingletonWrapper::template prep<R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg) >; \
232             return boost::bind \
233             ( \
234                 T2nSingletonWrapper::template real_exec<R>, \
235                 boost::bind( p, f BOOST_PP_COMMA_IF(n) \
236                 BOOST_PP_ENUM(n, _GEN_PLACEHOLDER, ~ ) ) \
237             ); \
238         } // eo exec
239
240         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
241
242 #undef _GEN_EXEC
243 #undef _GEN_PLACEHOLDER
244
245 };
246
247 // create an t2n_exec-method for each possible number of parameters
248 #define _GEN_EXEC(z,n,d) \
249         template< class Client, typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
250         static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > t2n_exec \
251         ( \
252             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
253         ) \
254         { \
255             return T2nSingletonWrapper<Client>::exec(f); \
256         } // eo exec
257
258         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
259
260 #undef _GEN_EXEC
261 #undef _GEN_PLACEHOLDER
262
263 }
264 #endif