networkPong.cpp

This example shows how to distribute an application across a network.

pong.hpp (identical to the pong.hpp from the pong.cpp example!)

// acedia includes
#include "acedia/actor.hpp"
#include "acedia/invoker.hpp"

// other includes
#include <iostream>
#include <boost/cstdint.hpp>

typedef boost::int32_t i32;

// the ball that's sended between Ping and Pong
ACEDIA_DECLARE_CASE_TUPLE1((GameBall), hits, i32)

class Ping : public acedia::Actor
{

    void returnPass(i32 hits)
    {
        // lastReceivedMessage() returns the current processed
        // message in 'original' form which then is converted to
        // a string and printed to stdout
        std::cout << "Ping: "
                  << lastReceivedMessage().toString().const_str()
                  << std::endl;

        // inrement the hit counter and pass the ball back to Pong
        reply(GameBall(hits+1));
    }

public:

    // this is the only method you must override in your own actor class
    virtual void act()
    {
        using acedia::on;

        acedia::Invoker i;

        // Step by step:
        // on<GameBall>() => declares a pattern that matches instances
        //                   of GameBall
        // .unbox()       => unboxing a tuple means: throw away the tuple
        //                   and handle the elements; in this case
        //                   all further operations see only a i32
        // >>             => if the left hand rule does match then invoke
        //                   the following callback
        // method(...)    => declare a callback that invokes a member method
        i.add(on<GameBall>().unbox() >> method(&Ping::returnPass));

        // call receiveAndInvoke until the actor dies
        for (;;) receiveAndInvoke(i);
    }

};

class Pong : public acedia::Actor
{

    acedia::ActorRef ping;

    void returnPass(i32 hits)
    {
        std::cout << "Pong: "
                  << lastReceivedMessage().toString().const_str()
                  << std::endl;
        reply(GameBall(hits+1));
    }

    void die()
    {
        // do exit with a non-normal reason
        // a non-normal exit reason will cause all links to exit too
        // in this case Ping will die because it's linked with Pong and Pong
        // dies with USER_DEFINED_REASON (and not with NORMAL_EXIT)
        doExit(acedia::exit_reasons::USER_DEFINED_REASON);
    }

public:

    Pong(acedia::ActorRef myPing) : ping(myPing) { }

    virtual void act()
    {
        using acedia::on;
        using acedia::isGt;

        // kickoff
        send(ping, GameBall(0));

        acedia::Invoker i;

        i
         // on a GameBall message unbox it and check the guard isGt(9)
         // which returns true if GameBall::hits() > 9
         .add(on<GameBall>().unbox().guard(isGt((i32) 9))

              // invoke Pong::die if the patter does match
              // note that die does not take any arguments but is a valid
              // invoke target because you can ignore any number of
              // message elements
              //
              // e.g.:
              // you receive a message <int, string, float, double>
              // valid callback signatures are:
              // (int, string, float, double)
              // (string, float, double)
              // (float, double)
              // (double)
              // ()
              >> method(&Pong::die))

         // the second rule is only checked if the first rule did not match
         .add(on<GameBall>().unbox() >> method(&Pong::returnPass))
        ;

        for (;;) receiveAndInvoke(i);
    }

};
#include "pong.hpp"
#include "acedia/acedia_network.hpp"

// implementation of the client side
void client(const char *host, boost::uint16_t port)
{
    using namespace acedia;
    using std::cout;
    using std::endl;
    std::pair<ActorRef, ConnectionError> res = remoteActor(host, port);
    if (res.first.isValid())
    {
        ActorRef ping = res.first;
        link(spawn<Pong>(ping), ping);
        waitForAllActorsDone();
        shutdown();
    }
    else
    {
        std::cout << "Could not connect to remote actor: "
                  << acedia::asString(res.second)
                  << std::endl;
    }
}

// implementation of the server side
void server(boost::uint16_t port)
{
    acedia::ActorRef ping = acedia::spawn<Ping>();
    acedia::PublishingResult res = acedia::publish(ping, port);
    if (!res.successful())
    {
        std::cout << "Could not publish Ping at port " << port
             << ": " << res.errorAsString()
             << std::endl;
        acedia::shutdown();
        return;
    }
    acedia::waitForAllActorsDone();
    acedia::shutdown();
}

// if you call this program with one argument then you start the server and
// the argument must be the port
// if you call this program with two arguments then you start the client
// and the arguments are host and port
int main(int argc, char **argv)
{
    if (argc == 2)
    {
        server(atoi(argv[1]));
    }
    else if (argc == 3)
    {
        client(argv[1], atoi(argv[2]));
    }
    else
    {
        std::cout << "usage:" << std::endl
                  << "server: " << argv[0] << " {host} {port}" << std::endl
                  << "client: " << argv[0] << " {port}" << std::endl;
    }
    return 0;
}

Generated by  doxygen 1.6.2