Turi Create
4.0
|
CPPIPC is a client-server architecture, where
The basic idea of CPPIPC is the triple split of
To communicate reliably, it is crucial that both client and server have a common view of an object. To achieve this, we go with the common base class model: a base class is used to define an interface, and an implementation of the interface is provided on the server side. On the client side, a proxy class (which also implements the interface) is provided, except that this proxy class simply forwards all calls to the server.
For instance (this code will not actually work):
However, implementing a proxy class is hugely annoying (you have to implement the class all over again), easy to get wrong (since there will be a decent amount of copy and pasting), and there are several rules and stuff (registration must be called, etc). We therefore provide a collection of magic macros that make the implementation effort easier, though at the cost of some of the most annoying looking compiler messages at minor syntax errors.
Here, we are going to implement a basic "counter" service. i.e. the server is going to maintain an integer counter which we can read, and add to.
Begin by #including the appropriate headers
We can then generate the interface and proxy objects using the GENERATE_INTERFACE_AND_PROXY macro.
For instance, here, we created an interface class called "basic_counter_base", and a proxy class called "basic_counter_proxy" which extends the base class. The base class defines 3 functions,
A few observations.
The calls are serialized using Turi Create's serialization interface. i.e. all arguments and return types must be serializable. See serialization for details.
Known Limitations:
"std::map<std::string, size_t>"
you will need to typedef it to a type name with no commas before using it in the macro. For instance:To see why you should always use the magic macros and not write your own proxy/base object, see the documentation for GENERATE_INTERFACE_AND_PROXY
Now, the macro defines the base and proxy objects, but you have to define your own implementation object. There are no rules for the implementation object besides that you must inherit from the base, and implement all the functions defined in the base.
And you are done!
Put it all in a header (for simplicity). In a real system the implementation will be placed separately from the proxy so the client compilation does not inherit all the server's dependencies.
Next create 2 cpp files, one server, and one client.
The "ipc:///tmp/cppipc_server_test" is a ZeroMQ endpoint address and is the address the server will listen and wait on for connections. ipc://
is an "interprocess socket" endpoint and should point to a filename (preferably non-existent, and unused). It can also be tcp://
[ip address]:[portnumber] to have the server wait on a TCP/IP connection. Note that it must be an IP address in this case. ZeroMQ does not do hostname resolution.
The register_type call is used to register the "basic_counter_base" type with the server, and also inform the server how to construct an instance of the object when asked by a client. (As an alternative to defining a factory function, a lambda can be used as well).