Table of Contents

Server-side Components

Registry

rpc::registry contains a collection of functions mapped by key.

void nothing()
{
}

int inc(int i)
{
    return ++i;
}

int add2( int i, int j )
{
    return i+j;
}

void inc_inplace(int &i)
{
    ++i;
}

namespace rpc = boost::rpc;
using namespace boost::asio;

void network_rpc_test()
{
    // make a registry keyed on string id-s and using binary_archive serialization
    rpc::registry<std::string> reg;

    // register the functions
    reg.set<void ()>("nothing", nothing);
    reg.set<int (int)>("inc", inc);
    reg.set<int (int, int)>("add2", add2);

Server

rpc::server will wait for incoming connections using a specified connection protocol. Upon making a successful connection, it will assign it a dedicated rpc::registry_server object.

    // create a server for the registry using a simple connection acceptor
    rpc::server<rpc::registry<std::string>, rpc::simple_acceptor> server(reg, 1097);

rpc::simple_acceptor just grants a connection to anyone that asks for it. The Acceptor is a template parameter, so different connection/authentication protocols could be used (although they should probably be in a different library, since they are not necessarily specific to RPC).

Client-side components

Client

rpc::client negotiates a connection to the server and can service calls to the registry on the server side.

    // create a client which will connect to the server through the network.
    rpc::connecting_client<rpc::registry<std::string>, rpc::simple_connector> client(
        ip::tcp::endpoint(ip::address::from_string("127.0.0.1"), 1097));

Making a Call and Getting the Results

rpc::call prepares a call by storing the function id and serializing the parameters. A call is then given to the client by passing the function id and parameters through the rpc::call class. The call will return a call handler which can be used to get the return result or verify that the function has completed.

The behavior is currently specified as follows:

In the future, the marshaling of return values/completion acknowledgement and "out" arguments will be separated.

    // make some function calls
    
    // embed the function id for a void () call
    rpc::call<std::string, void ()> call_nothing("nothing");
    // this call is made asynchrously, nothing will be marshaled back because
    // there are no "out" arguments and the returned handler is not stored.
    client(call_nothing);

    // here, since the returned call handler is stored in an acknowledgement,
    // upon completion the server will marshal back only a confirmation of the completion.
    rpc::acknowledgement_ptr ack = client(call_nothing);
    BOOST_CHECK_NO_THROW(ack->completion().get());

    // embed the function id and parameter for an int (int) call
    rpc::call<std::string, int (int)> call_inc__1("inc",  1);
    // if the returned handler is ignored, nothing is marshaled back:
    client(call_inc__1);

    // if the returned handler is stored in an acknowledgement, only a confirmation of completion
    // is marshaled back.
    ack = client(call_inc__1);
    BOOST_CHECK_NO_THROW(ack->completion().get());

    // if the returned handler is stored in a proper handler, the return value will be marshaled back
    rpc::async_returning_handler<int>::ptr handler_int = client(call_inc__1);
    boost::future<int> future_int(handler_int->return_promise());
    BOOST_CHECK_EQUAL(future_int, 2);

    // handler returners are imlplicitly convertible to futures, which will carry the returned value
    boost::future<int> result_inc = client(call_inc__1);
    BOOST_CHECK_EQUAL(result_inc, 2);

    // handler returners are also convertible to values, which immediately
    // get assigned the value of the return value future, making the call synchronous
    int inced1 = client(call_inc__1);
    BOOST_CHECK_EQUAL(inced1,  2);

    int i = 1;
    // embed the function id and parameters for a void (int &) call
    rpc::call<std::string, void (int &)> call_inc_inplace__i("inc_inplace", i);
    // since the call sends by reference, it will act syncronously - i will immediately be assigned
    // the value of the future int carrying the modified value of the "out" parameter.
    client(call_inc_inplace__i);
    BOOST_CHECK_EQUAL(i, 2);

    // embed the function id and parameter for an int (int, int) call
    rpc::call<std::string, int (int, int)> call_add2__5_6("add2",  5, 6);
    // handler returners are imlplicitly convertible to futures, which will carry the returned value
    boost::future<int> result_add = client(call_add2__5_6);
    BOOST_CHECK_EQUAL(result_add, 11);

    // this call sends the "in" value of an "in/out" paramater through a future (messy)
    boost::promise<int> prom;
    prom.set(200);
    boost::future<int> inced(prom);
    rpc::call<std::string, void (int &)> call_inc_inplace__inced("inc_inplace", inced);
    client(call_inc_inplace__inced);
    BOOST_CHECK_EQUAL(inced, 201);

} // end void network_marshal_test

Generated on Sun May 20 15:47:56 2007 by  doxygen 1.5.2