From: Gerd v. Egidy Date: Fri, 26 Sep 2008 13:17:06 +0000 (+0000) Subject: libt2n: (gerd) more documentation-polishing X-Git-Tag: v0.4^0 X-Git-Url: http://developer.intra2net.com/git/?p=libt2n;a=commitdiff_plain;h=9a5d7790b094439b9a6f16983e20493c0e43ee02;hp=393e7b700efffc748e593661b365ead414a341c1 libt2n: (gerd) more documentation-polishing --- diff --git a/README b/README index ca202d2..00c06ea 100644 --- a/README +++ b/README @@ -16,8 +16,12 @@ shared memory or other mechanisms. Serializing all the complex objects is done with Boost serialization. The complete manual is generated by doxygen in the doc directory. -Changes -------- +Changes from 0.3 +---------------- +* Add Client-Wrappers, see examples-codegen/example1-client-wrapper how to + use them +* API-incompatible change: + command_client now takes a client_connection pointer instead of a reference * Fix build of documentation for "out of tree" builds * Fixed a bug with nested namespaces * Fixed a bug with incomplete writes (Robert Huitl) diff --git a/doc/index.doc b/doc/index.doc index febb386..136caea 100644 --- a/doc/index.doc +++ b/doc/index.doc @@ -51,7 +51,7 @@ Using libt2n and its code generator only the server procedure implementations ha - \anchor notes2 [2] remote procedure call (RPC), http://en.wikipedia.org/wiki/Remote_procedure_call and "THE RPC MODEL" http://www.faqs.org/rfcs/rfc1050.html - \anchor notes3 - [3] Figure: Remote procedure call overview, http://jan.netcomp.monash.edu.au/webservices/rpc_stub.png, Jan Newmarch "Web services" http://jan.netcomp.monash.edu.au/webservices/tutorial.html + [3] Figure: Remote procedure call overview, http://jan.newmarch.name/webservices/rpc_stub.png , Jan Newmarch "Web services" http://jan.newmarch.name/webservices/tutorial.html - \anchor notes4 [4] in fact gccxml is used to parse the C++ code and the XML output of gccxml is used as input for the code generator - \anchor notes5 @@ -69,7 +69,7 @@ Using libt2n and its code generator only the server procedure implementations ha \section server Example server program and client library \par The procedure to export (input for the code generator - libt2n-codegen): t2nexample.cpp: - First the procedure to export is defined. It is marked for export with the attribute macro "LIBT2N_EXPORT". In this example the procedure throws an exception if the input string is "throw". The exception is passed back to the client transparently. Otherwise some text is appended and returned. + First the procedure to export is defined. It is marked for export with the attribute macro \ref LIBT2N_EXPORT. In this example the procedure throws an exception if the input string is "throw". The exception is passed back to the client transparently. Otherwise some text is appended and returned. \include example1/t2nexample.cpp \par Required includes go into the group header file: t2nexample.hxx: @@ -123,4 +123,21 @@ ok $ kill %1 $ rm socket \endverbatim + + \section wrapper The Client-Wrapper + The interfaces can be called directly in the way outlined above. But this means you have to take care of connection errors between client and server + at each call, possibly try to reconnect and so on. Libt2n provides the Client-Wrapper to ease this. It is a way to select a error handling strategy + once and use it automatically for all calls invoked through the Wrapper. Tough error-handling is the common usecase, the Client-Wrapper could be used + to execute any user-provided code before and after a call to the server is made. + + The other feature that the Client-Wrapper provides is a connection-singleton. T2n (currently) only offers single-threaded servers. So if you use methods of a T2n-server in a program, you usually only want to maintain one common connection to this server - even if it is accessed from different parts/modules/classes/... of your program. The Client-Wrapper is initialized with a \ref libt2n::ConnectionWrapper. + + This \ref libt2n::ConnectionWrapper takes the error-handling strategy (e.g. reconnect-then-throw) and everything needed to establish a connection (e.g. socket name or host and tcp-port) as parameters. A connection is established at the first actual request to the server and re-used for following requests. You don't need to pass around client-handles and the like to your classes or methods, finding the right wrapper is done via the global singleton created for each server-interface initialized for the wrapper. + + This example shows how to use the Client-Wrapper: + + \include example1-client-wrapper/client.cpp + +The details of the Client-Wrapper can be found in the \ref libt2n::T2nSingletonWrapper, but beware, the code is full of ugly templates and template-construction-defines. + */ diff --git a/examples-codegen/Makefile.am b/examples-codegen/Makefile.am index 6150e99..207620e 100644 --- a/examples-codegen/Makefile.am +++ b/examples-codegen/Makefile.am @@ -1,2 +1,2 @@ # automatically generated by ./test-build-install-use -EXTRA_DIST = $(srcdir)/example1/* $(srcdir)/example1-client/* $(srcdir)/example2/* $(srcdir)/example2-client/* $(srcdir)/README +EXTRA_DIST = $(srcdir)/example1/* $(srcdir)/example1-client/* $(srcdir)/example1-client-wrapper/* $(srcdir)/example2/* $(srcdir)/example2-client/* $(srcdir)/README diff --git a/examples-codegen/example1-client-wrapper/client.cpp b/examples-codegen/example1-client-wrapper/client.cpp index 86a2802..b9692d0 100644 --- a/examples-codegen/example1-client-wrapper/client.cpp +++ b/examples-codegen/example1-client-wrapper/client.cpp @@ -22,8 +22,6 @@ int main(int argc, char** argv) wraptype::set_connection(std::auto_ptr (new libt2n::ReconnectSocketWrapper("./socket"))); - std::string result; - // execute a function via t2n. The wrapper will open a connection to the server if none // exists or try to reconnect if the existing one doesn't answer std::cout << t2n_exec(&cmd_group_t2nexample_client::testfunc)("hello") << endl; diff --git a/src/client_wrapper.hxx b/src/client_wrapper.hxx index 3370615..cb333db 100644 --- a/src/client_wrapper.hxx +++ b/src/client_wrapper.hxx @@ -169,6 +169,13 @@ class T2nSingletonWrapperMessages calls using this connection with an error-handling strategy (e.g. to reconnect when the connection broke). The source looks very complicated due to heavy use of templates, look at the 3rd codegen example to see how to use it. + + @par Example + Calling remote methods is usually done via t2n_exec, this saves you from always + specifying which T2nSingletonWrapper-template to use when calling T2nSingletonWrapper::exec + @code + t2n_exec(&cmd_group_t2nexample_client::testfunc)("the answer is %d",42) + @endcode */ template< class Client > class T2nSingletonWrapper : public T2nSingletonWrapperMessages @@ -179,6 +186,7 @@ class T2nSingletonWrapper : public T2nSingletonWrapperMessages static std::auto_ptr SingletonObject; static std::auto_ptr WrappedConnection; + /// @cond // create a prep-method for each possible number of parameters #define _GEN_ARG(z,n,d) Arg ## n arg ##n #define _GEN_PREP(z,n,d) \ @@ -200,6 +208,7 @@ class T2nSingletonWrapper : public T2nSingletonWrapperMessages #undef _GEN_PREP #undef _GEN_ARG + /// @endcond T2nSingletonWrapper(std::auto_ptr stub) { @@ -241,6 +250,9 @@ class T2nSingletonWrapper : public T2nSingletonWrapperMessages public: + /** @brief tell the wrapper which connection to use + @param wrappedConnection the connection to establish when needed + */ static void set_connection(std::auto_ptr wrappedConnection) { WrappedConnection=wrappedConnection; @@ -249,15 +261,19 @@ class T2nSingletonWrapper : public T2nSingletonWrapperMessages if (SingletonObject.get() != NULL) SingletonObject.reset(); } + + /// return a pointer to the ConnectionWrapper currently in use static ConnectionWrapper* get_connection_wrapper(void) { return WrappedConnection.get(); } + /// manually establish the connection without actually executing a call static void ensure_singleton_there(void) { if (SingletonObject.get() == NULL) init(); } + /// @cond // create an exec-method for each possible number of parameters #define _GEN_PLACEHOLDER(z,n,d) BOOST_PP_CAT(_,BOOST_PP_ADD(n,1)) #define _GEN_EXEC(z,n,d) \ @@ -282,9 +298,11 @@ class T2nSingletonWrapper : public T2nSingletonWrapperMessages #undef _GEN_EXEC #undef _GEN_PLACEHOLDER + /// @endcond }; +/// @cond // create an t2n_exec-method for each possible number of parameters #define _GEN_EXEC(z,n,d) \ template< class Client, typename R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \ @@ -300,6 +318,7 @@ class T2nSingletonWrapper : public T2nSingletonWrapperMessages #undef _GEN_EXEC #undef _GEN_PLACEHOLDER +/// @endcond } #endif diff --git a/src/command_server.cpp b/src/command_server.cpp index 035c334..91011fd 100644 --- a/src/command_server.cpp +++ b/src/command_server.cpp @@ -158,6 +158,7 @@ void command_server::handle_packet(const std::string& packet, server_connection* /** @brief handle incoming commands @param[in,out] usec_timeout wait until new data is found, max timeout usecs. -1: wait endless, 0: instant return + @param[out] usec_timeout_remaining microseconds from the timeout that were not used */ void command_server::handle(long long usec_timeout, long long* usec_timeout_remaining) { diff --git a/src/server.hxx b/src/server.hxx index aa0b232..684cee8 100644 --- a/src/server.hxx +++ b/src/server.hxx @@ -130,7 +130,7 @@ class server @retval true if new data was found (does not mean that the received data is a complete packet though) */ - virtual bool fill_buffer(long long usec_timeout=-1, long long* timeout_remaining=NULL)=0; + virtual bool fill_buffer(long long usec_timeout=-1, long long* usec_timeout_remaining=NULL)=0; void cleanup(); diff --git a/src/socket_handler.cpp b/src/socket_handler.cpp index dfaa23c..b2bbcd1 100644 --- a/src/socket_handler.cpp +++ b/src/socket_handler.cpp @@ -159,6 +159,7 @@ void socket_handler::set_write_timeout(long long new_write_timeout) @param[in,out] usec_timeout wait until new data is found, max timeout usecs. -1: wait endless 0: return instantly + @param[out] usec_timeout_remaining microseconds from the timeout that were not used */ bool socket_handler::data_waiting(long long usec_timeout,long long* usec_timeout_remaining) { @@ -200,11 +201,12 @@ bool socket_handler::data_waiting(long long usec_timeout,long long* usec_timeout @param[in,out] usec_timeout wait until new data is found, max timeout usecs. -1: wait endless 0: return instantly + @param[out] usec_timeout_remaining microseconds from the timeout that were not used */ -bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout, long long *timeout_remaining) +bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout, long long *usec_timeout_remaining) { // fast path for timeout==0 - if (usec_timeout==0 || data_waiting(usec_timeout,timeout_remaining)) + if (usec_timeout==0 || data_waiting(usec_timeout,usec_timeout_remaining)) return fill_buffer(buffer); else return false; diff --git a/src/socket_handler.hxx b/src/socket_handler.hxx index 623f31c..799913e 100644 --- a/src/socket_handler.hxx +++ b/src/socket_handler.hxx @@ -38,7 +38,7 @@ class socket_handler socket_type_value socket_type; - bool data_waiting(long long usec_timeout,long long *timeout_remaining=NULL); + bool data_waiting(long long usec_timeout,long long *usec_timeout_remaining=NULL); void wait_ready_to_write(int socket, long long write_block_timeout); protected: @@ -58,7 +58,7 @@ class socket_handler virtual void close(); - bool fill_buffer(std::string& buffer, long long usec_timeout, long long*timeout_remaining=NULL); + bool fill_buffer(std::string& buffer, long long usec_timeout, long long* usec_timeout_remaining=NULL); bool fill_buffer(std::string& buffer); public: diff --git a/src/socket_wrapper.hxx b/src/socket_wrapper.hxx index ebb85f6..067931e 100644 --- a/src/socket_wrapper.hxx +++ b/src/socket_wrapper.hxx @@ -32,6 +32,12 @@ namespace libt2n { +/** @brief a basic implementation of ConnectionWrapper + + This is a basic version of a ConnectionWrapper which does not do any fancy + error handling or anything, it justs executes the regular calls. Use this + wrapper if you only want to use the singleton-feature of T2nSingletonWrapper. +*/ class BasicSocketWrapper : public ConnectionWrapper { protected: @@ -69,6 +75,11 @@ class BasicSocketWrapper : public ConnectionWrapper void set_logging(std::ostream *_logstream, log_level_values _log_level); }; +/** @brief a wrapper implementing reconnect-then-throw + + This ConnectionWrapper tries to reconnect to the server if something with the connection + goes wrong. If even reconnecting max_retries times does not help, an exception is thrown. +*/ class ReconnectSocketWrapper : public BasicSocketWrapper { public: @@ -87,6 +98,7 @@ class ReconnectSocketWrapper : public BasicSocketWrapper bool handle(command_client* stubBase, boost::function< void() > f); }; +/// a placeholder-client_connection which is closed all the time class dummy_client_connection : public client_connection { private: @@ -102,6 +114,12 @@ class dummy_client_connection : public client_connection { return false; } }; +/** @brief a wrapper implementing reconnect-then-ignore + + This ConnectionWrapper tries to reconnect to the server if something with the connection + goes wrong. If even reconnecting max_retries times does not help, the complete t2n-call is + ignored. The return value of the call will be created with the default constructor. +*/ class ReconnectIgnoreFailureSocketWrapper : public ReconnectSocketWrapper { private: