安排一个异步事件,该事件将在 stdin 在 boost::asio 中有等待数据时完成?

Schedule an asynchronous event that will complete when stdin has waiting data in boost::asio?

我将 boost::asio 与 ncurses 一起用于命令行游戏。游戏需要以固定的时间间隔在屏幕上绘制,其他操作(例如网络或文件操作)也会在需要时执行。所有这些事情都可以用 async_read()/async_write() 或 boost::asio.

上的等价物来完成

但是,我还需要读取键盘输入,(我认为)来自标准输入。在 ncurses 中读取输入的通常方法是调用 getch(),它可以配置为阻塞(等待直到有一个字符可供使用)或非阻塞(return 那里的标记值没有可用字符)模式。

使用阻塞模式需要 运行 getch() 在一个单独的线程上,这与 ncurses 不兼容。但是,使用非阻塞模式会导致我的应用程序在循环中消耗 CPU 时间,直到用户按下键盘。我读过 答案,这表明我们可以在 select() 调用中将 stdin 添加到文件描述符列表中,这将阻塞直到其中一个文件描述符有新数据。

由于我使用的是boost::asio,所以不能直接使用select()。我不能调用 async_read,因为那样会消耗字符,让 getch() 没有任何内容可读。 boost::asio 中是否有类似 async_read 的东西,但只是检查输入的存在而不使用它?

我认为您应该能够使用 posix 流描述符来监视文件描述符 0:

上的输入
ba::posix::stream_descriptor d(io, 0);
input_loop = [&](error_code ec) {
    if (!ec) {
        program.on_input();
        d.async_wait(ba::posix::descriptor::wait_type::wait_read, input_loop);
    }
};

那里,program::on_input() 会调用 getch() 而没有 timeout() 直到 returns ERR:

struct Program {
    Program() {
        initscr();
        ESCDELAY = 0;
        timeout(0);
        cbreak(); 

        noecho();
        keypad(stdscr, TRUE); // receive special keys

        clock   = newwin(2, 40, 0, 0);
        monitor = newwin(10, 40, 2, 0);

        syncok(clock, true);    // automatic updating
        syncok(monitor, true);

        scrollok(monitor, true); // scroll the input monitor window
    }
    ~Program() {
        delwin(monitor);
        delwin(clock);
        endwin();
    }

    void on_clock() {
        wclear(clock);

        char buf[32];
        time_t t = time(NULL);
        if (auto tmp = localtime(&t)) {
            if (strftime(buf, sizeof(buf), "%T", tmp) == 0) {
                strncpy(buf, "[error formatting time]", sizeof(buf));
            }
        } else {
            strncpy(buf, "[error getting time]", sizeof(buf));
        }

        wprintw(clock, "Async: %s", buf);
        wrefresh(clock);
    }

    void on_input() {
        for (auto ch = getch(); ch != ERR; ch = getch()) {
            wprintw(monitor, "received key %d ('%c')\n", ch, ch);
        }
        wrefresh(monitor);
    }

    WINDOW *monitor = nullptr;
    WINDOW *clock = nullptr;
};

使用下面的 main 程序你会 运行 它 10 秒(因为 Program 还不知道如何退出):

int main() {
    Program program;

    namespace ba = boost::asio;
    using boost::system::error_code;
    using namespace std::literals;

    ba::io_service io;
    std::function<void(error_code)> input_loop, clock_loop;

    // Reading input when ready on stdin
    ba::posix::stream_descriptor d(io, 0);
    input_loop = [&](error_code ec) {
        if (!ec) {
            program.on_input();
            d.async_wait(ba::posix::descriptor::wait_type::wait_read, input_loop);
        }
    };

    // For fun, let's also update the time
    ba::high_resolution_timer tim(io);
    clock_loop = [&](error_code ec) {
        if (!ec) {
            program.on_clock();
            tim.expires_from_now(100ms);
            tim.async_wait(clock_loop);
        }
    };

    input_loop(error_code{});
    clock_loop(error_code{});
    io.run_for(10s);
}

这个有效:

完整列表

#include <boost/asio.hpp>
#include <boost/asio/posix/descriptor.hpp>
#include <iostream>
#include "ncurses.h"

#define CTRL_R    18
#define CTRL_C    3
#define TAB       9
#define NEWLINE   10
#define RETURN    13
#define ESCAPE    27
#define BACKSPACE 127
#define UP        72
#define LEFT      75
#define RIGHT     77
#define DOWN      80

struct Program {
    Program() {
        initscr();
        ESCDELAY = 0;
        timeout(0);
        cbreak(); 

        noecho();
        keypad(stdscr, TRUE); // receive special keys

        clock   = newwin(2, 40, 0, 0);
        monitor = newwin(10, 40, 2, 0);

        syncok(clock, true);    // automatic updating
        syncok(monitor, true);

        scrollok(monitor, true); // scroll the input monitor window
    }
    ~Program() {
        delwin(monitor);
        delwin(clock);
        endwin();
    }

    void on_clock() {
        wclear(clock);

        char buf[32];
        time_t t = time(NULL);
        if (auto tmp = localtime(&t)) {
            if (strftime(buf, sizeof(buf), "%T", tmp) == 0) {
                strncpy(buf, "[error formatting time]", sizeof(buf));
            }
        } else {
            strncpy(buf, "[error getting time]", sizeof(buf));
        }

        wprintw(clock, "Async: %s", buf);
        wrefresh(clock);
    }

    void on_input() {
        for (auto ch = getch(); ch != ERR; ch = getch()) {
            wprintw(monitor, "received key %d ('%c')\n", ch, ch);
        }
        wrefresh(monitor);
    }

    WINDOW *monitor = nullptr;
    WINDOW *clock = nullptr;
};

int main() {
    Program program;

    namespace ba = boost::asio;
    using boost::system::error_code;
    using namespace std::literals;

    ba::io_service io;
    std::function<void(error_code)> input_loop, clock_loop;

    // Reading input when ready on stdin
    ba::posix::stream_descriptor d(io, 0);
    input_loop = [&](error_code ec) {
        if (!ec) {
            program.on_input();
            d.async_wait(ba::posix::descriptor::wait_type::wait_read, input_loop);
        }
    };

    // For fun, let's also update the time
    ba::high_resolution_timer tim(io);
    clock_loop = [&](error_code ec) {
        if (!ec) {
            program.on_clock();
            tim.expires_from_now(100ms);
            tim.async_wait(clock_loop);
        }
    };

    input_loop(error_code{});
    clock_loop(error_code{});
    io.run_for(10s);
}