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