Signals & multithreading

Boost signal and thread support. More...

Boost signal and thread support.

Along with providing access to the basic C functions of libplayerc in a C++ fashion, libplayerc++ also provides additional functionality along the lines of signaling and multithreading. The multithreaded ability of libplayerc++ allieves the developer from having to worry about allotting time to handle messaging. It also allows for the PlayerClient to act as a messaging loop for event driven programs. The signaling and multithreading ability of libplayerc++ is built from the Boost c++ libraries. This is relevant because we will be using boost semantincs for connecting the signals to the client. Much of this functionality can best be illustrated through the use of an example:

/*
Copyright (c) 2005, Brad Kratochvil, Toby Collett, Brian Gerkey, Andrew Howard, ...
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of the Player Project nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <iostream>
#include <boost/signal.hpp>
#include <boost/bind.hpp>
#include <libplayerc++/playerc++.h>

// we have a basic command line parser here
#include "args.h"

// These are our callback functions.
// Currently, they all must return void.
void cb1()
  { std::cout << "cb1" << std::endl; }

// Callbacks can also be passed parameters
void cb2(uint32_t &aI)
  { std::cout << "cb2 " << ++aI << std::endl; }

// we can also have callbacks in objects
class TestCb
{
    int mId;

  public:

    TestCb(int aId) : mId(aId) {};

    // We'll output the ID of the TestCb, so we can see
    // which object is being called.
    void Cb()
      { std::cout << "TestCb " << mId << std::endl; }

    void Cb(uint32_t aOpt)
      { std::cout << "TestCb " << mId << " " << aOpt << std::endl; }

};

// we'll use this to stop the client
void stop_cb(PlayerCc::PlayerClient* c, uint32_t &i)
{
  // after 10 iterations, stop the client
  if (++i>10)
  {
    std::cout << "stop: " << i << std::endl;
    c->Stop();
  }
}

int main(int argc, char** argv)
{
  parse_args(argc, argv);
  
  // it's always good to put the code in a try block
  // libplayerc++ throws a PlayerError exception when
  // it runs into trouble
  try
  {
    // let's setup a client
    // by default PlayerClient uses localhost and 6665
    PlayerCc::PlayerClient client(gHostname, gPort);
    PlayerCc::CameraProxy  cp(&client, 0);

    // Here, we're connecting a signal to a function.
    // We keep the connection_t so we can later disconnect.
    PlayerCc::ClientProxy::connection_t conn;
    conn = cp.ConnectReadSignal(&cb1);

    // Signals can also be connected without storing the connection_t,
    // but you can no longer disconnect them.
    // In order to use a callback with a argument, you need to bind
    // the argument to the callback.
    uint32_t count = 0;
    cp.ConnectReadSignal(boost::bind(&cb2, count));

    // here we're connecting a signal to a member function
    TestCb test1(1), test2(2);
    // just like functions, member functions can also be bound
    // with or without arguments
    cp.ConnectReadSignal(boost::bind(&TestCb::Cb, boost::ref(test1)));
    cp.ConnectReadSignal(boost::bind(&TestCb::Cb, boost::ref(test2), 1));

    // now, we should see some signals each time Read() is called for the
    // object that receives the data

    // libplayerc++ has three different ways for handling messages from the
    // server.  The first is by manually calling Read() each time we would
    // like to process data.
    std::cout << "Read()" << std::endl;
    for (uint32_t i=0; i<10; ++i)
    {
      client.Read();
      // an example of disconnecting a signal
      if (4==i)
        cp.DisconnectReadSignal(conn);
    }

    // The PlayerClient can also be used as a messaging loop through the use of
    // a blocking Run() method.  The correct way to tell Run() to quit is
    // through a stop callback.  In this case, we only have a single thread,
    // so we don't have to worry about multithreading
    std::cout << "Run()" << std::endl;

    // Let's connect our stop_cb() signal.  This signal tells the client
    // to exit after 10 iterations
    uint32_t i = 0;
    conn = cp.ConnectReadSignal(boost::bind(&stop_cb, &client, i));

    // Now, let's run the client.  This exits when the client->Stop() function
    // is called from the callback.
    client.Run();

    // We can also access the client in a multithreaded fashion.
    std::cout << "StartThread()" << std::endl;

    // let's first disconnect our previous stop
    cp.DisconnectReadSignal(conn);

    // Start the thread, which will handle all message processing.
    client.StartThread();

    // Sleep for 5 seconds.  During this time, callbacks should be
    // fired on every read and output should show up on the display.
    timespec sleep = {5, 0};
    nanosleep(&sleep, NULL);

    // Instead of sleeping here, we could also be sending commands and reading
    // directly from the proxy.
    for (uint32_t j=0; j<10; ++j)
    {
      cp.SaveFrame("test");
      // all proxies have a iostream operator
      std::cout << cp << std::endl;
    }

    // Now, let's stop the thread.  This function only returns after
    // the thread has been stopped
    client.StopThread();

    std::cout << "finished!" << std::endl;
  }
  catch (PlayerCc::PlayerError & e)
  {
    // let's output the error
    std::cerr << e << std::endl;
    return -1;
  }
  return 1;
}