2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
22 #ifndef __LIBT2N_CLIENT_WRAPPER
23 #define __LIBT2N_CLIENT_WRAPPER
27 #include <boost/config.hpp>
28 #include <boost/bind.hpp>
29 #include <boost/function.hpp>
30 #include <boost/preprocessor.hpp>
33 #include <command_client.hxx>
35 #ifndef T2N_SINGLETON_WRAPPER_MAX_ARGS
36 #define T2N_SINGLETON_WRAPPER_MAX_ARGS 9
42 /** @brief provide access to client_connection singletons and call-handling function
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.
48 class ConnectionWrapper
51 long long command_timeout_usec;
52 long long hello_timeout_usec;
55 log_level_values log_level;
56 std::ostream *logstream;
57 void set_logging_on_connection(client_connection& c);
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)
66 virtual ~ConnectionWrapper()
69 /** @brief return active connection, create new one if not existing
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.
76 virtual client_connection* get_connection()=0;
78 /** @brief this function is called on every execution of a method on a server
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
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.
89 virtual bool handle(command_client* stubBase, boost::function< void() > f)
95 long long get_command_timeout_usec(void)
96 { return command_timeout_usec; }
98 void set_command_timeout_usec(long long _command_timeout_usec)
99 { command_timeout_usec=_command_timeout_usec; }
101 long long get_hello_timeout_usec(void)
102 { return hello_timeout_usec; }
104 void set_hello_timeout_usec(long long _hello_timeout_usec)
105 { hello_timeout_usec=_hello_timeout_usec; }
107 virtual void set_logging(std::ostream *_logstream, log_level_values _log_level);
109 std::ostream* get_logstream(log_level_values level);
112 // contains the internal stuff needed for T2nWrapper
116 template< typename T >
123 struct TypeWrap< void >
128 template< typename R >
131 typedef boost::function< R() > FuncType;
136 Call( FuncType f, R& res ) : function(f), result( res ) {}
147 typedef boost::function< void() > FuncType;
148 typedef TypeWrap< void >::type ResultType;
153 Call( FuncType f, ResultType& res ) : function(f), result( res ) {}
158 result= ResultType();
161 } // eo namespace detail
163 class T2nSingletonWrapperMessages
166 static const char* NotInitializedMessage;
169 /** @brief wrap calls to server-side-functions with different error-handling strategies
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.
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
180 t2n_exec(&cmd_group_t2nexample_client::testfunc)("the answer is %d",42)
183 template< class Client >
184 class T2nSingletonWrapper : public T2nSingletonWrapperMessages
187 std::auto_ptr<Client> Stub;
189 static std::auto_ptr<T2nSingletonWrapper> SingletonObject;
190 static std::auto_ptr<ConnectionWrapper> WrappedConnection;
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 \
199 R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
200 BOOST_PP_COMMA_IF(n) \
201 BOOST_PP_ENUM( n, _GEN_ARG, ~ ) \
204 return boost::bind< R > \
206 f, _1 BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,arg) \
210 BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1) , _GEN_PREP, ~ )
216 T2nSingletonWrapper(std::auto_ptr<Client> stub)
223 if (WrappedConnection.get() == NULL)
224 throw std::logic_error(NotInitializedMessage);
226 std::auto_ptr<Client> stub(new Client(WrappedConnection->get_connection(),
227 WrappedConnection->get_command_timeout_usec(),
228 WrappedConnection->get_hello_timeout_usec()));
230 SingletonObject=std::auto_ptr<T2nSingletonWrapper>(new T2nSingletonWrapper(stub));
233 template< typename R >
235 typename detail::TypeWrap<R>::type real_exec( boost::function< R(Client*) > f)
237 ensure_singleton_there();
239 typename detail::TypeWrap<R>::type result;
241 // bind our Client-object and the local result
242 detail::Call<R> call( boost::bind( f, SingletonObject->Stub.get()), result );
244 // let the wrapper-handler call the fully-bound function
245 if (!WrappedConnection->handle(SingletonObject->Stub.get(),call))
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();
256 /** @brief tell the wrapper which connection to use
257 @param wrappedConnection the connection to establish when needed
259 static void set_connection(std::auto_ptr<ConnectionWrapper> wrappedConnection)
261 WrappedConnection=wrappedConnection;
263 // reset the singleton to NULL because the singleton must be constructed with current WrappedConnection
264 if (SingletonObject.get() != NULL)
265 SingletonObject.reset();
268 /// return a pointer to the ConnectionWrapper currently in use
269 static ConnectionWrapper* get_connection_wrapper(void)
270 { return WrappedConnection.get(); }
272 /// manually establish the connection without actually executing a call
273 static void ensure_singleton_there(void)
275 if (SingletonObject.get() == NULL)
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 \
286 R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
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) >; \
294 T2nSingletonWrapper::template real_exec<R>, \
295 boost::bind( p, f BOOST_PP_COMMA_IF(n) \
296 BOOST_PP_ENUM(n, _GEN_PLACEHOLDER, ~ ) ) \
300 BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
303 #undef _GEN_PLACEHOLDER
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 \
314 R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
317 return T2nSingletonWrapper<Client>::exec(f); \
320 BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
323 #undef _GEN_PLACEHOLDER