Example 4

In this example, we will explore the remote pointer and how we can pass it as remote method parameter. It introduces the items below:

  • get a remote pointer with bind method.
  • get a remote pointer with remote_cast method.
  • pass remote pointer to a remote method.
  • return remote pointer from a remote method.
  • pass many remote pointers to a remote method with a container.

We are going to create two remote interfaces which are rmt and remote_test. The remote_test interface will have method to accept and return rmt pointer. When the target object received pointer to rmt, it will invoke a method from this pointer.

First, create the rmt remote interface.

// rmt.hpp
#ifndef EXAMPLE_RMT_HPP
#define EXAMPLE_RMT_HPP
#include <remote/idl.hpp>
#include <string>

#define REMOTE_CLASS                        \
REMOTE_CLASS_BEGIN(rmt)                     \
REMOTE_METHOD_M1(void, invoke, std::string) \
REMOTE_CLASS_END
#include <remote/idl/class.hpp>

#define REMOTE_REGISTER_CLASS rmt
#include <remote/idl/register_class.hpp>

#endif
// rmt.cpp
#include <remote/make_tcp_binding.hpp>
#define REMOTE_IMPLEMENT_CLASS rmt
#include "rmt.hpp"

Then, the remote_test remote interface. Take note in the code below, we include remote/vector.hpp and remote/shared_ptr.hpp headers. This is needed to marshal the remote pointer in these containers when passed as parameter. We also need to include the header file for rmt interface.

// remote_test.hpp
#ifndef EXAMPLE_REMOTE_TEST_HPP
#define EXAMPLE_REMOTE_TEST_HPP

#include <remote/idl.hpp>
#include <remote/vector.hpp>       // for vector marshaller
#include <remote/shared_ptr.hpp>   // for shared_ptr marshaller
#include "rmt.hpp"                 // for rmt class declaration

#define REMOTE_CLASS                                                        \
REMOTE_CLASS_BEGIN(remote_test)                                             \
REMOTE_METHOD_M1(void, set_rmt_ptr, boost::shared_ptr<rmt>)                 \
REMOTE_METHOD_M0(boost::shared_ptr<rmt>, get_rmt_ptr)                       \
REMOTE_METHOD_M1(void, invoke_all, std::vector<boost::shared_ptr<rmt>>)     \
REMOTE_CLASS_END
#include <remote/idl/class.hpp>

#define REMOTE_REGISTER_CLASS remote_test
#include <remote/idl/register_class.hpp>

#endif

In the implemenation file of remote_test interface, we must include the rmt.hpp header file again before defining REMOTE_IMPLEMENT_CLASS because we don't want to generate implementation code for rmt interface in this translation unit.

// remote_test.cpp
#include "rmt.hpp"  // include this header file again here

#include <remote/make_tcp_binding.hpp>
#define REMOTE_IMPLEMENT_CLASS remote_test
#include "remote_test.hpp"

In the server application, we implement server_target class to bind with rmt interface and test_target class to bind with remote_test interface.

// server.cpp
#include "remote_test.hpp"
#include <remote/server.hpp>
#include <remote/make_tcp_binding.hpp>
#include <boost/make_shared.hpp>
#include <iostream>

class server_target
{
    std::string m_name;
public:
    server_target(std::string name)
    : m_name(name)
    {}

    void invoke(std::string msg)
    {
        std::cout << "invoke " << m_name << " from " << msg < std::endl;
    }
};

class test_target
{
    boost::shared_ptr<rmt> m_stored_ptr;
public:

    void set_rmt_ptr(boost::shared_ptr<rmt> r)
    {
        r->invoke("server");
        m_stored_ptr = r;
    }

    boost::shared_ptr<rmt> get_rmt_ptr()
    {
        return m_stored_ptr;
    }

    void invoke_all(std::vector<boost::shared_ptr<rmt> > v)
    {
        // invoke all the remote pointer in container
        std::for_each(v.begin(), v.end(), invoke);
    }

    static void invoke(boost::shared_ptr<rmt>& r)
    {
        r->invoke("server");
    }
};

int main()
{
    remote::server server;

    // create some named remote services
    server.bind<rmt>(boost::make_shared<server_target>("s1"), "s1");
    server.bind<rmt>(boost::make_shared<server_target>("s2"), "s2");
    server.bind<remote_test>(boost::make_shared<test_target>(), "test");

    server.start(remote::make_tcp_binding(8888));

    std::cin.get();
    return 0;
}

In the server main function, we create two named rmt services bind to server_target so that client can get remote pointer that point to server side object. A named remote_test service is also created.


In the client application, we also create a client_target class to bind with rmt interface to create remote pointer that point to client side object.

// client.cpp
#include "remote_test.hpp"
#include <remote/session.hpp>
#include <remote/make_tcp_binding.hpp>

class client_target
{
    std::string m_name;
public:
    client_target(std::string name)
    : m_name(name)
    {}

    void invoke(std::string msg)
    {
        std::cout << "invoke " << m_name << " from " << msg << std::endl;
    }
};

int main()
{
    remote::session session;

    client_target tc1("c1");
    boost::shared_ptr<client_target> tc2 = boost::make_shared<client_target>("c2");

    // using 2 different ways to convert target pointer to remote pointer
    // "bind" always return a shared_ptr regardless of what type of target pointer
    // "remote_cast" always return same type of pointer as target pointer
    boost::shared_ptr<rmt> c1 = session.bind<rmt>(&tc1);
    boost::shared_ptr<rmt> c2 = session.remote_cast<rmt>(tc2);

    session.start(remote::make_tcp_binding("127.0.0.1", 8888));
    if(session.wait_for_ready() != remote::session::started)
        return -1;

    // get some server side remote pointer
    boost::shared_ptr<rmt> s1 = session.get<rmt>("s1");
    boost::shared_ptr<rmt> s2 = session.get<rmt>("s2");
    boost::shared_ptr<remote_test> test = session.get<remote_test>("test");

    // send a remote pointer to be invoked and stored at server side
    // then get back the stored pointer from server to be invoked here
    test->set_rmt_ptr(c1);
    boost::shared_ptr<rmt> rc1 = test->get_rmt_ptr();
    rc1->invoke("client");

    // same as above but this time we send a
    // remote pointer pointing to server object
    test->set_rmt_ptr(s1);
    boost::shared_ptr<rmt> rs1 = test->get_rmt_ptr();
    rs1->invoke("client");

    // send multiple remote pointers to be invoked at server
    // mixing pointers that point to objects from client and server.
    std::vector<boost::shared_ptr<rmt> > v;
    v.push_back(c1);
    v.push_back(s1);
    v.push_back(c2);
    v.push_back(s2);

    test->invoke_all(v);

    return 0;
}

This example shows that we can create remote pointer pointing to object that reside at either client or server process. And we can pass this pointer around both processes through remote method call. The target method that receive a remote pointer doesn't need to care whether the remote pointer is pointing to client or server side object. It can just invoke the method in the remote pointer parameter and the remote call will just invoke the correct target object.

Next Example