This example shows how to implement actors and send messages between them.
Output of this example:
Ping: Message(GameBall(hits = 0)) Pong: Message(GameBall(hits = 1)) Ping: Message(GameBall(hits = 2)) Pong: Message(GameBall(hits = 3)) Ping: Message(GameBall(hits = 4)) Pong: Message(GameBall(hits = 5)) Ping: Message(GameBall(hits = 6)) Pong: Message(GameBall(hits = 7)) Ping: Message(GameBall(hits = 8)) Pong: Message(GameBall(hits = 9)) Ping: Message(GameBall(hits = 10))
pong.hpp:
// 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" int main(int, char**) { using namespace acedia; // spawn ping ActorRef ping = spawn<Ping>(); // spawn pong as a link of ping to enforce ping to exit if pong dies link(ping, spawn<Pong>(ping)); // wait until ping and pong are done waitForAllActorsDone(); // quit acedia shutdown(); return 0; }