Wednesday, September 21, 2011

delayed_send

delayed_send and its brother delayed_reply are great if you need to poll a resource every x (milli)seconds while staying responsive to other messages or if you want to implement a "timed loop". Both accept std::chrono durations in seconds, milliseconds, microseconds and minutes. However, the accuracy depends on your scheduler, operating system, workload, etc., so you shouldn't rely on a microseconds-timing.

A good example for such a loop is an animation. In message oriented programming (and this is was libcppa is about), the most idiomatic way to implement such a loop is to send a message to yourself that is delayed by a predefined amount of time. If you receive that message, you do the next animation step and send another delayed message to yourself, and so on, until the animation is done.

Today's example is a terminal-animation with a dancing Kirby. Have fun!

#include <chrono>
#include <iostream>
#include <algorithm>
#include "cppa/cppa.hpp"

using std::cout;
using std::endl;
using namespace cppa;

// ASCII art figures
constexpr const char* figures[] = {
  "<(^.^<)",
  "<(^.^)>",
  "(>^.^)>"
};

// array of {figure, offset} pairs
constexpr size_t animation_steps[][2] = {
  {1,  7}, {0,  7}, {0,  6}, {0,  5}, {1,  5}, {2,  5}, {2,  6},
  {2,  7}, {2,  8}, {2,  9}, {2, 10}, {1, 10}, {0, 10}, {0,  9},
  {1,  9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13},
  {0, 12}, {0, 11}, {0, 10}, {0,  9}, {0,  8}, {0,  7}, {1,  7}
};

constexpr size_t animation_width = 20;

// "draws" an animation step: {offset_whitespaces}{figure}{padding}
void draw_kirby(size_t const (&animation)[2]) {
  cout.width(animation_width);
  cout << '\r';
  std::fill_n(std::ostream_iterator<char>{cout}, animation[1], ' ');
  cout << figures[animation[0]];
  cout.fill(' ');
  cout.flush();
}

void dancing_kirby() {
  // let's get it started
  send(self, atom("Step"));
  // iterate over animation_steps
  auto i = std::begin(animation_steps);
  receive_for(i, std::end(animation_steps)) (
    on<atom("Step")>() >> [&]() {
      draw_kirby(*i);
      // animate next step in 150ms
      delayed_send(self, std::chrono::milliseconds(150), atom("Step"));
    }
  );
}

int main() {
  cout << endl;
  dancing_kirby();
  cout << endl;
}

No comments:

Post a Comment