Skip to content

5.3 how to time the user

camio edited this page May 15, 2013 · 1 revision

Problem

How do you ask the user for input and output it as long as he responds in time?

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/array.hpp>
#include <boost/make_shared.hpp>

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

  // Turn off newline buffering
  ::termios term;
  ::tcgetattr( STDIN_FILENO, &term );
  term.c_lflag &= ~(ICANON);
  ::tcsetattr( STDIN_FILENO, TCSANOW, &term );

  asio::posix::stream_descriptor in( io, ::dup( STDIN_FILENO ) );
  asio::posix::stream_descriptor out( io, ::dup( STDOUT_FILENO ) );
  asio::async_write(
    out,
    asio::buffer( "Please type in your name: " ),
    [&]( const boost::system::error_code & ec, std::size_t transferred )
    {
      auto timer = boost::make_shared<boost::asio::deadline_timer>( io );
      timer->expires_from_now( boost::posix_time::seconds( 5 ) );
      timer->async_wait(
        [timer, &in, &out]( const boost::system::error_code & ec )
        {
          if( ec )
          {
            if( ec != asio::error::operation_aborted )
            {
              auto message = boost::make_shared<std::string>( "Error: " + ec.message() + "\n" );
              asio::async_write(
                out,
                asio::buffer( *message ),
                [&message]( const boost::system::error_code & ec, std::size_t transferred ) {} );
            }
          }
          else
          {
            in.cancel();
          }
        } );
      const std::size_t bufferSize = 10;
      auto response = boost::make_shared< boost::asio::streambuf >( bufferSize );
      response->prepare( bufferSize );
      asio::async_read_until(
        in,
        *response,
        '\n',
        [&io,&in,&out,response,timer](
          const boost::system::error_code & ec,
          std::size_t transferred )
        {
          timer->cancel();
          if( ec )
          {
            if( ec == asio::error::not_found ) {
              asio::async_write(
                out,
                asio::buffer( "\nYour name is too long, try a nickname\n" ),
                []( const boost::system::error_code & ec, std::size_t transferred ) {} );
            }
            else if( ec == asio::error::operation_aborted )
            {
              asio::async_write(
                out,
                asio::buffer( "\nYou took too long. Bye.\n" ),
                []( const boost::system::error_code & ec, std::size_t transferred ) {} );
            }
            else
            {
              auto message = boost::make_shared<std::string>( "Error: " + ec.message() + "\n" );
              asio::async_write(
                out,
                asio::buffer( *message ),
                [&message]( const boost::system::error_code & ec, std::size_t transferred ) {} );
            }
          }
          else
          {
            std::istream is( response.get() );
            std::string name;
            std::getline( is, name, '\n' );
            auto message = boost::make_shared<std::string>(
              "Hello " + name + "!\n" );
            asio::async_write(
              out,
              asio::buffer( *message ),
              [&message]( const boost::system::error_code & ec, std::size_t transferred ) {} );
          }
        } );
    } );
  io.run();
}

Discussion

Notice how the timer is explicitly captured by the timer timeout callback even though it isn't used. This is required because the timer's destruction will implicitly cancel it. Generally we need to think about the lifetime of objects whenever they are passed to asynchronous operations.

Clone this wiki locally