You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于C++20协程实现带交互终端命令的技术疑问

基于C++20协程实现终端命令交互功能的解决方案

问题1:命令代码的编写位置及co_yield/co_return的使用

实际命令逻辑(如del /p的删除确认流程)需编写在专属协程函数内部(例如del_command,而非通用的make_command)。

  • co_yield:用于暂停协程执行,向终端传递所需输入类型(e_command_input)。每次需要用户输入时,通过co_yield抛出需求,协程暂停,直到终端提供输入后恢复。
  • co_return:当命令逻辑执行完成(如用户确认删除或取消操作)时,用co_return结束协程;无返回值时直接写co_return;。

示例协程函数(模拟del /p命令):

auto del_command(const std::string& pattern) -> OS_Command {
    std::cout << "确认删除所有" << pattern << "文件吗?(y/n): ";
    // 暂停协程,告知终端需要单个字符输入,恢复后直接获取输入结果
    auto input_result = co_yield OS_Command::e_command_input::SINGLE_CHAR;
    
    char confirm = std::get<char>(input_result);
    if (confirm == 'y' || confirm == 'Y') {
        std::cout << "正在删除文件..." << std::endl;
        // 执行实际删除逻辑
    } else {
        std::cout << "操作已取消" << std::endl;
    }
    
    co_return; // 结束协程
}

问题2:传递输入类型给Terminal并获取用户输入

需扩展OS_Command和promise_type,实现输入类型传递与结果回传的双向交互:

步骤1:扩展promise_type

添加输入类型、结果的存储变量,并实现yield_value返回可等待对象,让协程恢复后直接获取输入结果:

#include <variant>

class OS_Command
{
public:
    enum class e_command_input { SINGLE_CHAR, STRING };

    struct promise_type;
    using t_handle = std::coroutine_handle<promise_type>;

    // 协程恢复时获取输入结果的可等待对象
    struct InputAwaiter {
        promise_type& m_promise;
        bool await_ready() const noexcept { return false; } // 立即暂停
        void await_suspend(t_handle) const noexcept {}      // 无需额外暂停处理
        auto await_resume() const noexcept {                // 恢复时返回输入结果
            return m_promise.m_input_result;
        }
    };

    struct promise_type
    {
        auto get_return_object() -> OS_Command {
            return OS_Command{ t_handle::from_promise(*this) };
        }

        auto initial_suspend() -> std::suspend_always { return {}; }
        auto final_suspend() noexcept -> std::suspend_always { return {}; }
        void return_void() {}
        void unhandled_exception() { m_except = std::current_exception(); }

        // 存储当前需要的输入类型
        e_command_input m_required_input{};
        // 存储终端返回的输入结果(支持字符/字符串)
        std::variant<char, std::string> m_input_result{};
        std::exception_ptr m_except{};

        // 处理co_yield:保存输入类型并返回可等待对象
        auto yield_value(e_command_input input_type) -> InputAwaiter {
            m_required_input = input_type;
            return InputAwaiter{*this};
        }
    };

    // ... 原有OS_Command构造、析构、start方法 ...

    // 供终端获取当前所需输入类型
    e_command_input get_required_input() const {
        return m_handle.promise().m_required_input;
    }

    // 向协程传递单个字符输入
    void set_char_input(char c) {
        m_handle.promise().m_input_result = c;
    }

    // 向协程传递字符串输入
    void set_string_input(const std::string& s) {
        m_handle.promise().m_input_result = s;
    }

    // 检查协程是否已完成
    bool is_done() const {
        return m_handle.done();
    }

private:
    t_handle m_handle;
};

步骤2:终端驱动协程的逻辑

在终端主循环中控制协程执行流程:

int main()
{
    std::cout << "main: Before command" << std::endl;

    auto cmd = del_command("*.bak");
    cmd.start();

    // 驱动协程直到完成
    while (!cmd.is_done()) {
        auto input_type = cmd.get_required_input();
        
        if (input_type == OS_Command::e_command_input::SINGLE_CHAR) {
            char c;
            std::cin >> c;
            cmd.set_char_input(c);
        } else if (input_type == OS_Command::e_command_input::STRING) {
            std::string s;
            std::cin >> s;
            cmd.set_string_input(s);
        }

        // 恢复协程执行
        cmd.m_handle.resume();
    }

    std::cout << "main: After command" << std::endl;
    return 0;
}

问题3:make_command是否为真正的协程?它是工厂函数吗?

  • 是真正的协程:只要函数体内包含co_await、co_yield或co_return任意一种,就会被编译器识别为C++20协程。你的make_command包含co_await std::suspend_never{};,因此属于协程。
  • 是工厂函数:它的作用是创建并返回协程句柄的包装器(OS_Command对象),让调用方可以控制协程的启动、暂停与恢复,符合工厂函数的定义。但更合理的做法是为每个命令编写专属协程工厂函数(如del_command、rename_command),而非通用的make_command。

内容的提问来源于stack exchange,提问作者darknight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 00:35:59