Signals & multithreading
[libplayerc++]
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:
#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(uint &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(uint aOpt) { std::cout << "TestCb " << mId << " " << aOpt << std::endl; } }; // we'll use this to stop the client void stop_cb(PlayerCc::PlayerClient* c, uint &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. uint 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 (uint 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 uint 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 (uint 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; }