Search code examples
c++boostboost-asio

asio service handler for stdin keypress


I have adapted step 3 of the Boost asio tutorial to run forever, and display "tick" and "tock" once per second instead of the counter:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

void print(const boost::system::error_code& /*e*/,
    boost::asio::deadline_timer* t, int* count)
{
    if( !((*count) % 2) )
        std::cout << "tick\n";
    else
        std::cout << "tock\n";

    ++(*count);

    t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
    t->async_wait(boost::bind(print,
          boost::asio::placeholders::error, t, count));
}

int main()
{
  boost::asio::io_service io;

  int count = 0;
  boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
  t.async_wait(boost::bind(print,
        boost::asio::placeholders::error, &t, &count));

  io.run();

  std::cout << "Final count is " << count << "\n";

  return 0;
}

Now I want to asynchronously be able to handle a keypress on stdin. Is there an io_service handler I can use to respond to keypresses, without blocking sleeps or waits?

For example, I'd like to be able to implement a handler function similar to:

void handle_keypress(const boost::error_code&,
    char c)
{
    std::cout << "Tap '" << c << "'\n";
}

And I would expect my invocation of this handler to be something along the lines of:

  char c = 0;
  boost::asio::stdin_receiver sr(io);
  st.async_wait(boost::bind(handle_keypress, boost::asio::placeholders::error, &c));

  io.run();

Is this something I can do with asio, either by using a builtin service handler, or writing my own?

EDIT, ELABORATION:

I have seen this question, but the linked-to code in the accpeted answer simply does this in main:

 while (std::cin.getline(

The application I'm writing isn't this simple tick-tock-tap gizmo I've outlined above, but will be a multicast server. Several worker threads will be sending packets to multicast groups, responding to messages from the main thread, and sending messages back to the main thread. The application, in turn, will be "driven" by input from the stdin -- for example, when the user presses the "P" key, multicast broadcast will be paused, and when the hit "Q" the whole thing will shut down. In the main thread, all I'll do in response to these inputs is send messages to the worker threads.

The while loop above won't work in my scenario because while it's waiting for stdin input from the user, the main thread will not be able to process messages coming in from the worker threads. Some of those messages from the worker thread will generate output to stdout.


Solution

  • There is always the option of handling stdin in a separate thread and posting any keypresses to your main event loop via io_service::dispatch

    This function is used to ask the io_service to execute the given handler.

    The io_service guarantees that the handler will only be called in a thread in which the run(), run_one(), poll() or poll_one() member functions is currently being invoked.