Initial cmake conversion of the libt2n project except the example projects. The unit...
[libt2n] / src / client_wrapper.hxx
... / ...
CommitLineData
1/*
2Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4The software in this package is distributed under the GNU General
5Public License version 2 (with a special exception described below).
6
7A copy of GNU General Public License (GPL) is included in this distribution,
8in the file COPYING.GPL.
9
10As a special exception, if other files instantiate templates or use macros
11or inline functions from this file, or you compile this file and link it
12with other works to produce a work based on this file, this file
13does not by itself cause the resulting work to be covered
14by the GNU General Public License.
15
16However the source code for this file must still be made available
17in accordance with section (3) of the GNU General Public License.
18
19This exception does not invalidate any other reasons why a work based
20on this file might be covered by the GNU General Public License.
21*/
22#ifndef __LIBT2N_CLIENT_WRAPPER
23#define __LIBT2N_CLIENT_WRAPPER
24
25#include <functional>
26
27#include <boost/config.hpp>
28#include <boost/bind.hpp>
29#include <boost/function.hpp>
30#include <boost/preprocessor.hpp>
31
32#include <client.hxx>
33#include <command_client.hxx>
34
35#ifndef T2N_SINGLETON_WRAPPER_MAX_ARGS
36#define T2N_SINGLETON_WRAPPER_MAX_ARGS 9
37#endif
38
39namespace libt2n
40{
41
42/** @brief provide access to client_connection singletons and call-handling function
43
44 This is an abstact base class for use with T2nSingletonWrapper. It provides access
45 to the singleton-client_connection and offers a handle-method which is used for
46 every call to a method on a t2n-server.
47*/
48class ConnectionWrapper
49{
50 private:
51 long long command_timeout_usec;
52 long long hello_timeout_usec;
53
54 protected:
55 log_level_values log_level;
56 std::ostream *logstream;
57 void set_logging_on_connection(client_connection& c);
58
59 public:
60 ConnectionWrapper()
61 : log_level(none), logstream(NULL),
62 command_timeout_usec(command_client::command_timeout_usec_default),
63 hello_timeout_usec(command_client::hello_timeout_usec_default)
64 { }
65
66 virtual ~ConnectionWrapper()
67 { }
68
69 /** @brief return active connection, create new one if not existing
70
71 Return a pointer to an active client_connection. Use detail-data
72 stored within the derived class to create a new connection if needed,
73 otherwise return an alredy active connection. The derived class has
74 to take care of destroying the connection when not needed anymore.
75 */
76 virtual client_connection* get_connection()=0;
77
78 /** @brief this function is called on every execution of a method on a server
79
80 @param stubBase pointer to the command_client executing the call
81 @param f boost::function object containing the method to call and all parameters
82 @retval true if the call was successful and the original return-value of the called function can be used
83 false if T2nSingletonWrapper has to create a return-value with the default-constructor
84
85 T2nSingletonWrapper will call this function on every execution of a server-side
86 method. This version will just call the function without any special treatment.
87 You can overload this function to implement different error handling strategies.
88 */
89 virtual bool handle(command_client* stubBase, boost::function< void() > f)
90 {
91 f();
92 return true;
93 }
94
95 long long get_command_timeout_usec(void)
96 { return command_timeout_usec; }
97
98 void set_command_timeout_usec(long long _command_timeout_usec)
99 { command_timeout_usec=_command_timeout_usec; }
100
101 long long get_hello_timeout_usec(void)
102 { return hello_timeout_usec; }
103
104 void set_hello_timeout_usec(long long _hello_timeout_usec)
105 { hello_timeout_usec=_hello_timeout_usec; }
106
107 virtual void set_logging(std::ostream *_logstream, log_level_values _log_level);
108
109 std::ostream* get_logstream(log_level_values level);
110};
111
112// contains the internal stuff needed for T2nWrapper
113namespace detail
114{
115
116 template< typename T >
117 struct TypeWrap
118 {
119 typedef T type;
120 };
121
122 template< >
123 struct TypeWrap< void >
124 {
125 typedef int type;
126 };
127
128 template< typename R >
129 struct Call
130 {
131 typedef boost::function< R() > FuncType;
132
133 FuncType function;
134 R& result;
135
136 Call( FuncType f, R& res ) : function(f), result( res ) {}
137
138 void operator()()
139 {
140 result= function();
141 }
142 };
143
144 template< >
145 struct Call<void>
146 {
147 typedef boost::function< void() > FuncType;
148 typedef TypeWrap< void >::type ResultType;
149
150 FuncType function;
151 ResultType& result;
152
153 Call( FuncType f, ResultType& res ) : function(f), result( res ) {}
154
155 void operator()()
156 {
157 function();
158 result= ResultType();
159 }
160 };
161} // eo namespace detail
162
163class T2nSingletonWrapperMessages
164{
165 protected:
166 static const char* NotInitializedMessage;
167};
168
169/** @brief wrap calls to server-side-functions with different error-handling strategies
170
171 Template class to access a process-wide singleton server-connection and to wrap all
172 calls using this connection with an error-handling strategy (e.g. to reconnect when
173 the connection broke). The source looks very complicated due to heavy use of templates,
174 look at the 3rd codegen example to see how to use it.
175
176 @par Example
177 Calling remote methods is usually done via t2n_exec, this saves you from always
178 specifying which T2nSingletonWrapper-template to use when calling T2nSingletonWrapper::exec
179 @code
180 t2n_exec(&cmd_group_t2nexample_client::testfunc)("the answer is %d",42)
181 @endcode
182*/
183template< class Client >
184class T2nSingletonWrapper : public T2nSingletonWrapperMessages
185{
186 private:
187 std::auto_ptr<Client> Stub;
188
189 static std::auto_ptr<T2nSingletonWrapper> SingletonObject;
190 static std::auto_ptr<ConnectionWrapper> WrappedConnection;
191
192 /// @cond
193 // create a prep-method for each possible number of parameters
194#define _GEN_ARG(z,n,d) Arg ## n arg ##n
195#define _GEN_PREP(z,n,d) \
196 template< typename R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
197 static boost::function< R(Client*) > prep \
198 ( \
199 R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
200 BOOST_PP_COMMA_IF(n) \
201 BOOST_PP_ENUM( n, _GEN_ARG, ~ ) \
202 ) \
203 { \
204 return boost::bind< R > \
205 ( \
206 f, _1 BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,arg) \
207 ); \
208 } // eo prep
209
210 BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1) , _GEN_PREP, ~ )
211
212#undef _GEN_PREP
213#undef _GEN_ARG
214 /// @endcond
215
216 T2nSingletonWrapper(std::auto_ptr<Client> stub)
217 {
218 Stub=stub;
219 }
220
221 static void init()
222 {
223 if (WrappedConnection.get() == NULL)
224 throw std::logic_error(NotInitializedMessage);
225
226 std::auto_ptr<Client> stub(new Client(WrappedConnection->get_connection(),
227 WrappedConnection->get_command_timeout_usec(),
228 WrappedConnection->get_hello_timeout_usec()));
229
230 SingletonObject=std::auto_ptr<T2nSingletonWrapper>(new T2nSingletonWrapper(stub));
231 }
232
233 template< typename R >
234 static
235 typename detail::TypeWrap<R>::type real_exec( boost::function< R(Client*) > f)
236 {
237 ensure_singleton_there();
238
239 typename detail::TypeWrap<R>::type result;
240
241 // bind our Client-object and the local result
242 detail::Call<R> call( boost::bind( f, SingletonObject->Stub.get()), result );
243
244 // let the wrapper-handler call the fully-bound function
245 if (!WrappedConnection->handle(SingletonObject->Stub.get(),call))
246 {
247 // create an result with default-constructor if the handler could not
248 // successfully do a call but no exception occured
249 result=typename detail::TypeWrap<R>::type();
250 }
251 return result;
252 }
253
254 public:
255
256 /** @brief tell the wrapper which connection to use
257 @param wrappedConnection the connection to establish when needed
258 */
259 static void set_connection(std::auto_ptr<ConnectionWrapper> wrappedConnection)
260 {
261 WrappedConnection=wrappedConnection;
262
263 // reset the singleton to NULL because the singleton must be constructed with current WrappedConnection
264 if (SingletonObject.get() != NULL)
265 SingletonObject.reset();
266 }
267
268 /// return a pointer to the ConnectionWrapper currently in use
269 static ConnectionWrapper* get_connection_wrapper(void)
270 { return WrappedConnection.get(); }
271
272 /// manually establish the connection without actually executing a call
273 static void ensure_singleton_there(void)
274 {
275 if (SingletonObject.get() == NULL)
276 init();
277 }
278
279 /// @cond
280 // create an exec-method for each possible number of parameters
281#define _GEN_PLACEHOLDER(z,n,d) BOOST_PP_CAT(_,BOOST_PP_ADD(n,1))
282#define _GEN_EXEC(z,n,d) \
283 template< typename R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
284 static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > exec \
285 ( \
286 R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
287 ) \
288 { \
289 boost::function<R(Client*)>(*p)(R(Client::*)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
290 BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg)) \
291 = &T2nSingletonWrapper::template prep<R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg) >; \
292 return boost::bind \
293 ( \
294 T2nSingletonWrapper::template real_exec<R>, \
295 boost::bind( p, f BOOST_PP_COMMA_IF(n) \
296 BOOST_PP_ENUM(n, _GEN_PLACEHOLDER, ~ ) ) \
297 ); \
298 } // eo exec
299
300 BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
301
302#undef _GEN_EXEC
303#undef _GEN_PLACEHOLDER
304 /// @endcond
305
306};
307
308/// @cond
309// create an t2n_exec-method for each possible number of parameters
310#define _GEN_EXEC(z,n,d) \
311 template< class Client, typename R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
312 static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > t2n_exec \
313 ( \
314 R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
315 ) \
316 { \
317 return T2nSingletonWrapper<Client>::exec(f); \
318 } // eo exec
319
320 BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
321
322#undef _GEN_EXEC
323#undef _GEN_PLACEHOLDER
324/// @endcond
325
326}
327#endif