Вопрос по c++, boost-asio, boost – Сервисный обработчик ASIO для стандартного нажатия клавиш

7

Я адаптировалшаг 3 из учебника Boost Asio бежать вечно и отображать «галочку»; и & lt; tock & quot; один раз в секунду вместо счетчика:

#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;
}

Теперь я хочу асинхронно иметь возможность обрабатывать нажатие клавиш на стандартный ввод. Есть ли обработчик io_service, который я могу использовать, чтобы реагировать на нажатия клавиш, не блокируя сны или ожидания?

Например, я хотел бы иметь возможность реализовать функцию-обработчик, аналогичную следующей:

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

И я ожидаю, что мой вызов этого обработчика будет выглядеть примерно так:

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

  io.run();

Это то, что я могу сделать с asio, используя встроенный обработчик службы или написав свой собственный?

EDIT, ELABORATION:

я виделэтот вопрос, но связанный с ним код в ответе просто делает это вmain:

 while (std::cin.getline(

Приложение, которое я пишу, не является такой простой штуковиной, о которой я говорил выше, но будет сервером многоадресной рассылки. Несколько рабочих потоков будут отправлять пакеты группам многоадресной рассылки, отвечать на сообщения из основного потока и отправлять сообщения обратно в основной поток. Приложение, в свою очередь, будет «управляемым»; с помощью ввода из стандартного ввода - например, когда пользователь нажимает «P» ключ, многоадресная трансляция будет приостановлена, и когда нажмете "Q" все это закроется. В основном потоке все, что я буду делать в ответ на эти входные данные, - это отправлять сообщения рабочим потокам.

while Цикл выше не будет работать в моем сценарии, потому что пока он ожидает ввода от стандартного ввода от пользователя, основной поток не сможет обрабатывать сообщения, поступающие из рабочих потоков. Некоторые из этих сообщений из рабочего потока будут генерировать вывод на стандартный вывод.

Вы посмотрели на пример posix_chat_client, который описан как & quot; Следующий POSIX-специфичный чат-клиент демонстрирует, как использовать класс posix :: stream_descriptor для выполнения консольного ввода и вывода. & Quot;boost.org/doc/libs/1_49_0/doc/html/boost_asio/examples.html Ralf
Не уверен, правильно ли я вас понял, но в этом примере boost :: asio_service :: run вызывается в другом потоке, следовательно, ввод-вывод asio все равно будет обрабатываться? Ralf
@Ralf: Спасибо за ваш комментарий. Я просто редактировал свой пост об этом, когда вы вводили свой комментарий. Я сделал, и, если я не понял пример, я не думаю, что он делает то, что я хочу сделать. Пожалуйста, смотрите мое редактирование выше. John Dibling
@Ralf:run вызывается в другом потоке, но в не-POSIX примереcin.getline находится в главной теме. Я хочу упомянуть решение, которое работает как в Windows, так и в Linux, и POSIX-версия клиента чата#ifdefушел John Dibling

Ваш Ответ

2   ответа
4

клиент чата posix использует цикл while или вызываетstd::getline, который является примером кода, на который вы ссылались в моем предыдущем ответе. Возможно, вы имеете в виду другой пример? В любом случае вам не нужно использоватьio_service::dispatch или даже отдельная тема. Встроенные средства дескриптора потока работают просто отлично. Увидетьмой предыдущий ответ на похожий вопрос: используйтеposix::stream_descriptor и назначитьSTDIN_FILENO к этому. использованиеasync_read и обрабатывать запросы в обработчиках чтения.

Я изменил ваш пример кода одним способом для достижения этой цели.

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.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));
}

class Input : public boost::enable_shared_from_this<Input>
{
public:
    typedef boost::shared_ptr<Input> Ptr;

public:
    static void create(
            boost::asio::io_service& io_service
            )
    {
        Ptr input(
                new Input( io_service )
                );
        input->read();
    }

private:
    explicit Input(
            boost::asio::io_service& io_service
         ) :
        _input( io_service )
    {
        _input.assign( STDIN_FILENO );
    }

    void read()
    {
        boost::asio::async_read(
                _input,
                boost::asio::buffer( &_command, sizeof(_command) ),
                boost::bind(
                    &Input::read_handler,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred
                    )
                );
    }

    void read_handler(
            const boost::system::error_code& error,
            const size_t bytes_transferred
            )
    {
        if ( error ) {
            std::cerr << "read error: " << boost::system::system_error(error).what() << std::endl;
            return;
        }

        if ( _command != '\n' ) {
            std::cout << "command: " << _command << std::endl;
        }

        this->read();
    }

private:
    boost::asio::posix::stream_descriptor _input;
    char _command;
};

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));

  Input::create( io);

  io.run();

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

  return 0;
}

компилировать, связывать и запускать

samm:stackoverflow samm$ g++ -I /opt/local/include stdin.cc -L /opt/local/lib -lboost_system -Wl,-rpath,/opt/local/lib
samm:stackoverflow samm$ echo "hello world" | ./a.out
command: h
command: e
command: l
command: l
command: o
command:  
command: w
command: o
command: r
command: l
command: d
read error: End of file
tick
tock
tick
tock
tick
tock
tick
tock
tick
tock
^C
samm:stackoverflow samm$
@cmeerw Я не увидел требования Windows, указанные в вопросе, после прочтения комментариев в другом ответе я вижу это сейчас. В любом случаеstream_descriptor это элегантный способ решить вопрос Джона о платформах, поддерживающих его.
Но Джону нужно решение, которое также работает на Windows.
3

любых нажатий клавиш в ваш основной цикл событий черезio_service :: отправка

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.

Я мое окончательное решение, от которого я отказалсяasio поскольку он просто не имеет необходимых мне услуг. В любом случае, если бы я использовалasio Вот как я должен был это сделать. John Dibling
+1: Да, есть эта опция, и из того, что я читал повсюду, похоже, что это единственный вариант - по крайней мере, при использовании boost. Тем не менее, это приносит новую проблему - как вы говоритеstdin- нить, что пора умирать? Он заблокирован во время чтения. Я мог бы отправить «таблетку с ядом» вниз по стандарту, но я бы хотел менее хакерский путь. John Dibling

Похожие вопросы