|
libt2n 0.6
|
In this example we create two packages using the autotools:
// include automatically generated code #include "t2nexample_common.hxx" LIBT2N_EXPORT std::string testfunc(std::string str) { // exceptions derived from libt2n::t2n_exception are passed to the client transparently if (str=="throw") throw libt2n::t2n_runtime_error("throw me around"); return str+", testfunc() was here"; }
#include <string> #include <boost/serialization/string.hpp>
#include <signal.h> #include <socket_server.hxx> #include <command_server.hxx> // for group_command_server // the automatically generated server side code (cmd_group_t2nexample class) #include "t2nexample_server.hxx" int main(int argc, char** argv) { // don't kill the server on broken pipe signal(SIGPIPE, SIG_IGN); // create local socket server (a.k.a "unix domain socket") // if you want to to create a tcp/ip server you pass the port to the constructor // (for details take a look at the socket_server class documentation) libt2n::socket_server ss("./socket"); libt2n::group_command_server<cmd_group_t2nexample> cs(ss); // handle requests while(true) cs.handle(); return 0; }
AC_INIT(configure.in) AM_INIT_AUTOMAKE(libt2n-example1, 0.1) AC_LANG_CPLUSPLUS AC_PROG_CXX AM_PROG_LIBTOOL dnl check for libt2n (library and code generator) LIBT2N_CHECK(libt2n >= 0.2) AC_OUTPUT(Makefile)
# not a GNU package. You can remove this line, if you have all needed files, that a GNU package needs AUTOMAKE_OPTIONS = foreign # our dependencies INCLUDES = @LIBT2N_CFLAGS@ LDADD = @LIBT2N_LIBS@ # list your command groups (seperated by spaces) CMDGROUPS = t2nexample # for each command group list the files to parse for LIBT2N_EXPORT t2nexample_GROUP = t2nexample.cpp # unfortunately we can't set those from variables (because they are parsed by automake) # for each group build a client library from generated source file(s) # (if you have automake >= 1.5 you might wish to use nodist_ here) # Note: the library name must match the group name libt2nexample_la_SOURCES = t2nexample_client.cpp lib_LTLIBRARIES = libt2nexample.la # build server program bin_PROGRAMS = libt2n-example1-server # (if you have automake >= 1.5 you might wish to use nodist_ here) # nodist_server_SOURCES = t2nexample_server.cpp libt2n_example1_server_SOURCES = \ server.cpp $(t2nexample_GROUP) \ t2nexample_server.cpp # Make libt2n-gccxml.sh available # as libt2n is not installed yet PATH := $(top_srcdir):$(PATH) # include Makefile snippet doing all the magic include codegen.make
autoreconf -f -i && ./configure && make install
Using the library is as simple as using any other library using pkg-config (the pkg-config .pc file is created automatically by the included Makefile snippet)
AC_INIT(configure.in) AM_INIT_AUTOMAKE(libt2n-example1-client, 0.1) AC_LANG_CPLUSPLUS AC_PROG_CXX AM_PROG_LIBTOOL dnl check for the client library PKG_CHECK_MODULES(T2NEXAMPLE, t2nexample = 0.1) AC_OUTPUT(Makefile)
# not a GNU package. You can remove this line, if you have all needed files, that a GNU package needs AUTOMAKE_OPTIONS = foreign INCLUDES = @T2NEXAMPLE_CFLAGS@ LDADD = @T2NEXAMPLE_LIBS@ bin_PROGRAMS = libt2n-example1-client libt2n_example1_client_SOURCES = client.cpp
// for socket_client_connection #include <socket_client.hxx> // include generated library header #include "t2nexample_client.hxx" int main(int argc, char** argv) { // use a local socket (a.k.a "unix domain socket") // if you want to connect to a tcp/ip server you pass the port and server name to the constructor libt2n::socket_client_connection sc("./socket"); // this generated class has a method for each of our exported procedures cmd_group_t2nexample_client cc(&sc); bool throwok=false; // exceptions are passed back to the client transparently try { // call the remote procedure (we pass "throw" to cause a exception to be thrown) cc.testfunc("throw"); } catch(libt2n::t2n_runtime_error &e) { throwok=(std::string(e.what())=="throw me around"); } // call remote procedure and check the return value is correct return ( throwok && ( cc.testfunc("hello") == "hello, testfunc() was here" ) ) ? EXIT_SUCCESS : EXIT_FAILURE; }
autoreconf -f -i && ./configure && make install
$ cd /tmp $ file socket socket: cannot open `socket' (No such file or directory) $ libt2n-example1-server & [1] 7711 $ file socket socket: socket $ libt2n-example1-client && echo ok ok $ kill %1 $ rm socket
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 libt2n::ConnectionWrapper.
This 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:
// for a wrapped socket connection #include <socket_wrapper.hxx> // include generated library header #include "t2nexample_client.hxx" // define a type for more conveniant access typedef libt2n::T2nSingletonWrapper<cmd_group_t2nexample_client> wraptype; // static object which keeps the wrapper-singleton template<> std::auto_ptr<wraptype> wraptype::SingletonObject = std::auto_ptr<wraptype>(); // static object which keeps the connection template<> std::auto_ptr<libt2n::ConnectionWrapper> wraptype::WrappedConnection = std::auto_ptr<libt2n::ConnectionWrapper>(); int main(int argc, char** argv) { // tell the client-wrapper how to contact the server if a connection is requested wraptype::set_connection(std::auto_ptr<libt2n::ConnectionWrapper> (new libt2n::ReconnectSocketWrapper("./socket"))); // 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") << std::endl; return EXIT_SUCCESS; }
The details of the Client-Wrapper can be found in the libt2n::T2nSingletonWrapper, but beware, the code is full of ugly templates and template-construction-defines.
1.7.4