Change license from LGPL to GPL version 2 + linking exception. This fixes C++ templat...
[libt2n] / src / client_wrapper.hxx
CommitLineData
19facd85
TJ
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*/
45508d07
GE
22#ifndef __LIBT2N_CLIENT_WRAPPER
23#define __LIBT2N_CLIENT_WRAPPER
24
25#include <functional>
26
27#include <boost/config.hpp>
45508d07
GE
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
0ea6d528
GE
35#ifndef T2N_SINGLETON_WRAPPER_MAX_ARGS
36#define T2N_SINGLETON_WRAPPER_MAX_ARGS 9
45508d07
GE
37#endif
38
39namespace libt2n
40{
41
e162ddf2
GE
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*/
0ea6d528 48class ConnectionWrapper
45508d07 49{
e1614a6d
GE
50 private:
51 long long command_timeout_usec;
52 long long hello_timeout_usec;
45508d07 53
e1614a6d
GE
54 protected:
55 log_level_values log_level;
56 std::ostream *logstream;
57 void set_logging_on_connection(client_connection& c);
45508d07 58
e1614a6d
GE
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 { }
45508d07 65
e1614a6d
GE
66 virtual ~ConnectionWrapper()
67 { }
68
e162ddf2
GE
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 */
e1614a6d 76 virtual client_connection* get_connection()=0;
0ea6d528 77
e162ddf2
GE
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 */
fb3345ad 89 virtual bool handle(command_client* stubBase, boost::function< void() > f)
45508d07
GE
90 {
91 f();
fb3345ad 92 return true;
45508d07 93 }
0ea6d528 94
e1614a6d
GE
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
2a956e65 107 virtual void set_logging(std::ostream *_logstream, log_level_values _log_level);
e1614a6d
GE
108
109 std::ostream* get_logstream(log_level_values level);
45508d07
GE
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
0ea6d528 163class T2nSingletonWrapperMessages
45508d07 164{
0ea6d528
GE
165 protected:
166 static const char* NotInitializedMessage;
167};
45508d07 168
e162ddf2
GE
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.
9a5d7790
GE
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
e162ddf2 182*/
0ea6d528
GE
183template< class Client >
184class T2nSingletonWrapper : public T2nSingletonWrapperMessages
185{
186 private:
187 std::auto_ptr<Client> Stub;
45508d07 188
0ea6d528
GE
189 static std::auto_ptr<T2nSingletonWrapper> SingletonObject;
190 static std::auto_ptr<ConnectionWrapper> WrappedConnection;
45508d07 191
9a5d7790 192 /// @cond
45508d07
GE
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
0ea6d528 210 BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1) , _GEN_PREP, ~ )
45508d07
GE
211
212#undef _GEN_PREP
213#undef _GEN_ARG
9a5d7790 214 /// @endcond
45508d07 215
a64066eb
GE
216 T2nSingletonWrapper(std::auto_ptr<Client> stub)
217 {
218 Stub=stub;
219 }
220
221 static void init()
45508d07 222 {
0ea6d528
GE
223 if (WrappedConnection.get() == NULL)
224 throw std::logic_error(NotInitializedMessage);
45508d07 225
fb3345ad 226 std::auto_ptr<Client> stub(new Client(WrappedConnection->get_connection(),
0ea6d528
GE
227 WrappedConnection->get_command_timeout_usec(),
228 WrappedConnection->get_hello_timeout_usec()));
a64066eb
GE
229
230 SingletonObject=std::auto_ptr<T2nSingletonWrapper>(new T2nSingletonWrapper(stub));
45508d07
GE
231 }
232
233 template< typename R >
234 static
235 typename detail::TypeWrap<R>::type real_exec( boost::function< R(Client*) > f)
236 {
0ea6d528 237 ensure_singleton_there();
45508d07
GE
238
239 typename detail::TypeWrap<R>::type result;
fb3345ad
GE
240
241 // bind our Client-object and the local result
0ea6d528 242 detail::Call<R> call( boost::bind( f, SingletonObject->Stub.get()), result );
fb3345ad
GE
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 }
45508d07
GE
251 return result;
252 }
253
254 public:
255
9a5d7790
GE
256 /** @brief tell the wrapper which connection to use
257 @param wrappedConnection the connection to establish when needed
258 */
0ea6d528
GE
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 }
9a5d7790
GE
267
268 /// return a pointer to the ConnectionWrapper currently in use
e1614a6d 269 static ConnectionWrapper* get_connection_wrapper(void)
0ea6d528 270 { return WrappedConnection.get(); }
45508d07 271
9a5d7790 272 /// manually establish the connection without actually executing a call
0ea6d528 273 static void ensure_singleton_there(void)
45508d07 274 {
0ea6d528 275 if (SingletonObject.get() == NULL)
a64066eb 276 init();
45508d07
GE
277 }
278
9a5d7790 279 /// @cond
45508d07
GE
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)) \
0ea6d528 291 = &T2nSingletonWrapper::template prep<R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg) >; \
45508d07
GE
292 return boost::bind \
293 ( \
0ea6d528 294 T2nSingletonWrapper::template real_exec<R>, \
45508d07
GE
295 boost::bind( p, f BOOST_PP_COMMA_IF(n) \
296 BOOST_PP_ENUM(n, _GEN_PLACEHOLDER, ~ ) ) \
297 ); \
298 } // eo exec
299
0ea6d528 300 BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
45508d07
GE
301
302#undef _GEN_EXEC
303#undef _GEN_PLACEHOLDER
9a5d7790 304 /// @endcond
45508d07
GE
305
306};
307
9a5d7790 308/// @cond
0ea6d528
GE
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
9a5d7790 324/// @endcond
0ea6d528 325
45508d07
GE
326}
327#endif