Example 2

This example shows basic parameter passing supported by this library. Basically, any boost serializable type can be passed as parameter into remote method. This example introduces the items below:

  • Passing serializable object
  • Passing serializable container
  • Passing reference and pointer
  • Passing shared_ptr and weak_ptr
  • Binding shared_ptr to remote interface
  • Getting raw pointer of remote interface

The person class below is a serializable class that is passed as parameter into the methods in service class. We will make the service class accessible from client application.

// person.hpp
class person
{
public:
    std::string m_name;
    int m_age;

    person()
    : m_age(0)
    {}

    person(std::string name, int age)
    : m_name(name), m_age(age)
    {}

    void info() const
    {
        std::cout << m_name << " (" << m_age << ")" << std::endl;
    }

    template<typename Archive>
    void serialize(Archive& ar, unsigned int const ver)
    {
        ar & m_name & m_age;
    }
};

The service class is a simple class that prints info of all the person object passed into it's methods. If it is passed by pointer or reference, it increases the age of the person object.

// service.hpp
#include "person.hpp"
#include <boost/shared_ptr.hpp>
#include <vector>

class service
{
public:
    void pass_person(person p)
    {
        p.info();
    }

    void pass_people(std::vector<person> p)
    {
        typedef std::vector<person>::iterator iterator;
        for(iterator iter = p.begin(); iter != p.end(); ++iter)
            iter->info();
    }

    void pass_reference(person& r)
    {
        r.info();
        r.m_age++;
    }

    void pass_pointer(person* p)
    {
        if(p == 0)
            return;

        p->info();
        p->m_age++;
    }

    void pass_shared_ptr(boost::shared_ptr<person> s)
    {
        if(!s)
            return;

        s->info();
        s->m_age++;
    }

    void pass_weak_ptr(boost::weak_ptr<person> w)
    {
        boost::shared_ptr<person> s = w.lock();
        if(!s)
            return;

        s->info();
        s->m_age++;
    }
};

In the remote interface definition file for service, we must include serialization code for vector and shared_ptr to make them serializable via boost.serialization.

// remote_service.hpp
#ifndef REMOTE_SERVICE_HPP
#define REMOTE_SERVICE_HPP

#include "person.hpp"
#include <remote/idl.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>

#define REMOTE_CLASS                                                    \
REMOTE_CLASS_BEGIN(remote_service)                                      \
    REMOTE_METHOD_M1(void, pass_person, person)                         \
    REMOTE_METHOD_M1(void, pass_people, std::vector<person>)            \
    REMOTE_METHOD_M1(void, pass_reference, person&)                     \
    REMOTE_METHOD_M1(void, pass_pointer, person*)                       \
    REMOTE_METHOD_M1(void, pass_shared_ptr, boost::shared_ptr<person>)  \
    REMOTE_METHOD_M1(void, pass_weak_ptr, boost::weak_ptr<person>)      \
REMOTE_CLASS_END
#include <remote/idl/class.hpp>

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

#endif

And the implementation file for remote_service.

// remote_service.cpp
#include <remote/bindings/text_serializer.hpp>
#define REMOTE_IMPLEMENT_CLASS remote_service
#include "remote_service.hpp"

The person.hpp serves as data contract and the remote_service.hpp/cpp serve as the interface contract between the client and server applications. These 3 files must be included and linked into both the client and server applications.

After we have the remote interface class, creating server and client applications are simple.

// server.cpp
#include "service.hpp"
#include "remote_service.hpp"
#include <remote/server.hpp>
#include <remote/make_tcp_binding.hpp>
#include <boost/make_shared.hpp>


int main()
{
    remote::server server;

    // bind a shared_ptr to the server
    server.bind<remote_service>(boost::make_shared<service>(), "test");
    server.start(remote::make_tcp_binding("localhost", 9999));

    std::cin.get();
    return 0;
}
// client.cpp
#include "remote_service.hpp"
#include <remote/session.hpp>
#include <remote/make_tcp_binding.hpp>


int main()
{
    // create data
    person tom("tom", 13);
    person ben("ben", 18);

    std::vector<person> girls;
    girls.push_back(person("jenny", 9));
    girls.push_back(person("amanda", 10));

    auto eddy = boost::make_shared<person>("eddy", 30);
    auto andy = boost::make_shared<person>("andy", 38);

    // create session and connect to server
    remote::session session;
    session.start(remote::make_tcp_binding("localhost", 9999));
    session.wait_for_ready();

    // get a raw pointer of remote_service
    remote_service* service = session.get_raw<remote_service>("test");

    // use the remote pointer
    service->pass_person(tom);
    service->pass_people(girls);

    // pass by reference & pointer.
    service->pass_reference(tom);
    service->pass_pointer(&ben);
    service->pass_shared_ptr(eddy);
    service->pass_weak_ptr(andy);

    // passing null pointer is ok
    service->pass_pointer(0);
    service->pass_shared_ptr(boost::shared_ptr<person>());
    service->pass_weak_ptr(boost::shared_ptr<person>());

    // changes in referenced objects are updated
    assert(tom.m_age == 14);
    assert(ben.m_age == 19);
    assert(eddy->m_age == 31);
    assert(andy->m_age == 39);

    // release the raw pointer
    session.release(service);

    return 0;
}

It is advisable to always get shared pointer instead of raw pointer so that the session can manage the life time of the created object.

When a normal pointer or reference is passed to the remote method, the object it pointed to is sent to target site via serialization and any changes to the object at target site is serialized back to caller site to update the original object. So, make sure no dangling pointer is passed to the remote method.

The pointer parameter that is passed into the service object is not actually pointed to the original object at caller site but pointed to a temporary object at target site. Notice that the person info is printed at server application even though it is called via pointer.

Note:

It is possible to pass a pointer that is abstractly pointing to the remote object and make a method call to the actual remote object. This is realized by passing remote pointer as parameter.

Next Example