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