libt2n: (gerd) fix client-connection-logic, finish wrappers, all tests are working...
[libt2n] / src / client_wrapper.hxx
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>
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
32 #ifndef T2N_SINGLETON_WRAPPER_MAX_ARGS
33 #define T2N_SINGLETON_WRAPPER_MAX_ARGS 9
34 #endif
35
36 namespace libt2n
37 {
38
39 class ConnectionWrapper
40 {
41     private:
42         long long command_timeout_usec;
43         long long hello_timeout_usec;
44
45     protected:
46         log_level_values log_level;
47         std::ostream *logstream;
48         void set_logging_on_connection(client_connection& c);
49
50     public:
51         ConnectionWrapper()
52             : log_level(none), logstream(NULL), 
53               command_timeout_usec(command_client::command_timeout_usec_default),
54               hello_timeout_usec(command_client::hello_timeout_usec_default)
55             { }
56
57         virtual ~ConnectionWrapper()
58             { }
59
60         virtual client_connection* get_connection()=0;
61
62         virtual bool handle(command_client* stubBase, boost::function< void() > f)
63         {
64             f();
65             return true;
66         }
67
68         long long get_command_timeout_usec(void)
69             { return command_timeout_usec; }
70
71         void set_command_timeout_usec(long long _command_timeout_usec)
72             { command_timeout_usec=_command_timeout_usec; }
73
74         long long get_hello_timeout_usec(void)
75             { return hello_timeout_usec; }
76
77         void set_hello_timeout_usec(long long _hello_timeout_usec)
78             { hello_timeout_usec=_hello_timeout_usec; }
79
80         void set_logging(std::ostream *_logstream, log_level_values _log_level);
81
82         std::ostream* get_logstream(log_level_values level);
83 };
84
85 // contains the internal stuff needed for T2nWrapper
86 namespace detail
87 {
88
89     template< typename T >
90     struct TypeWrap
91     {
92         typedef T type;
93     };
94
95     template< >
96     struct TypeWrap< void >
97     {
98         typedef int type;
99     };
100
101     template< typename R >
102     struct Call
103     {
104         typedef boost::function< R() > FuncType;
105
106         FuncType function;
107         R& result;
108
109         Call( FuncType f, R& res ) : function(f), result( res ) {}
110
111         void operator()()
112         {
113             result= function();
114         }
115     };
116
117     template< >
118     struct Call<void>
119     {
120         typedef boost::function< void() > FuncType;
121         typedef TypeWrap< void >::type ResultType;
122
123         FuncType function;
124         ResultType& result;
125
126         Call( FuncType f, ResultType& res ) : function(f), result( res ) {}
127
128         void operator()()
129         {
130             function();
131             result= ResultType();
132         }
133     };
134 } // eo namespace detail
135
136 class T2nSingletonWrapperMessages
137 {
138     protected:
139         static const char* NotInitializedMessage;
140 };
141
142 template< class Client >
143 class T2nSingletonWrapper : public T2nSingletonWrapperMessages
144 {
145     private:
146         std::auto_ptr<Client> Stub;
147
148         static std::auto_ptr<T2nSingletonWrapper> SingletonObject;
149         static std::auto_ptr<ConnectionWrapper> WrappedConnection;
150
151         // create a prep-method for each possible number of parameters
152 #define _GEN_ARG(z,n,d) Arg ## n arg ##n
153 #define _GEN_PREP(z,n,d) \
154         template< typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
155         static boost::function< R(Client*) > prep \
156         ( \
157             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
158             BOOST_PP_COMMA_IF(n) \
159             BOOST_PP_ENUM( n, _GEN_ARG, ~ ) \
160         ) \
161         { \
162             return boost::bind< R > \
163                 ( \
164                     f, _1  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,arg) \
165                 ); \
166         } // eo prep
167
168         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1) , _GEN_PREP, ~ )
169
170 #undef _GEN_PREP
171 #undef _GEN_ARG
172
173         T2nSingletonWrapper(std::auto_ptr<Client> stub)
174         {
175             Stub=stub;
176         }
177
178         static void init()
179         {
180             if (WrappedConnection.get() == NULL)
181                 throw std::logic_error(NotInitializedMessage);
182
183             std::auto_ptr<Client> stub(new Client(WrappedConnection->get_connection(),
184                 WrappedConnection->get_command_timeout_usec(),
185                 WrappedConnection->get_hello_timeout_usec()));
186
187             SingletonObject=std::auto_ptr<T2nSingletonWrapper>(new T2nSingletonWrapper(stub));
188         }
189
190         template< typename R >
191         static
192         typename detail::TypeWrap<R>::type real_exec( boost::function< R(Client*) > f)
193         {
194             ensure_singleton_there();
195
196             typename detail::TypeWrap<R>::type result;
197
198             // bind our Client-object and the local result
199             detail::Call<R> call( boost::bind( f, SingletonObject->Stub.get()), result );
200
201             // let the wrapper-handler call the fully-bound function
202             if (!WrappedConnection->handle(SingletonObject->Stub.get(),call))
203             {
204                 // create an result with default-constructor if the handler could not
205                 // successfully do a call but no exception occured
206                 result=typename detail::TypeWrap<R>::type();
207             }
208             return result;
209         }
210
211     public:
212
213         static void set_connection(std::auto_ptr<ConnectionWrapper> wrappedConnection)
214         {
215             WrappedConnection=wrappedConnection;
216
217             // reset the singleton to NULL because the singleton must be constructed with current WrappedConnection
218             if (SingletonObject.get() != NULL)
219                 SingletonObject.reset();
220         }
221         static ConnectionWrapper* get_connection_wrapper(void)
222             { return WrappedConnection.get(); }
223
224         static void ensure_singleton_there(void)
225         {
226             if (SingletonObject.get() == NULL)
227                 init();
228         }
229
230         // create an exec-method for each possible number of parameters
231 #define _GEN_PLACEHOLDER(z,n,d) BOOST_PP_CAT(_,BOOST_PP_ADD(n,1))
232 #define _GEN_EXEC(z,n,d) \
233         template< typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
234         static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > exec \
235         ( \
236             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
237         ) \
238         { \
239             boost::function<R(Client*)>(*p)(R(Client::*)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
240                     BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg)) \
241                 = &T2nSingletonWrapper::template prep<R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg) >; \
242             return boost::bind \
243             ( \
244                 T2nSingletonWrapper::template real_exec<R>, \
245                 boost::bind( p, f BOOST_PP_COMMA_IF(n) \
246                 BOOST_PP_ENUM(n, _GEN_PLACEHOLDER, ~ ) ) \
247             ); \
248         } // eo exec
249
250         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
251
252 #undef _GEN_EXEC
253 #undef _GEN_PLACEHOLDER
254
255 };
256
257 // create an t2n_exec-method for each possible number of parameters
258 #define _GEN_EXEC(z,n,d) \
259         template< class Client, typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
260         static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > t2n_exec \
261         ( \
262             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
263         ) \
264         { \
265             return T2nSingletonWrapper<Client>::exec(f); \
266         } // eo exec
267
268         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
269
270 #undef _GEN_EXEC
271 #undef _GEN_PLACEHOLDER
272
273 }
274 #endif