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

C++协程通过promise类型传递值时触发段错误求助

C++协程段错误排查

问题描述

我实现了一个C++协程,通过三次co_await操作接收调用者使用返回类型Parser的feed()函数传入的新迭代器。这些值存储在promise类型中,并在Awaitable::resume()时传递给协程体内的引用迭代器(刚接触协程,如有更优实现方式欢迎指出)。但当前代码触发了段错误,相关代码如下:

#include <cstdio>
#include <coroutine>
#include <string_view>

struct Promise;

struct Parser : public std::coroutine_handle<Promise> {

    using promise_type = ::Promise;

    auto feed(std::convertible_to<std::string_view> auto&& view);

    ~Parser() {
        destroy();
    }
};

struct Awaitable {
    Promise* promise;
    std::string_view::iterator& start_;
    std::string_view::iterator& end_;

    Awaitable(std::string_view::iterator& start, std::string_view::iterator& end)
        : start_{ start }
        , end_{ end }
    { }
    auto await_ready() -> bool { printf("await_ready!\n"); return true; }
    auto await_suspend(std::coroutine_handle<Promise> handle) {
        promise = &handle.promise();
        printf("await_resume!\n");
    }
    auto await_resume() -> void;
};

struct Promise {
    std::pair<std::string_view::iterator, std::string_view::iterator> iterpair;

    auto get_return_object() -> Parser { return {Parser::from_promise(*this)}; };
    auto initial_suspend() noexcept -> std::suspend_never { return {}; }
    auto final_suspend() noexcept -> std::suspend_always { return {}; }
    auto await_transform(std::pair<std::string_view::iterator, std::string_view::iterator> iterpair) -> Awaitable { return {iterpair.first, iterpair.second}; }
    auto return_void() { printf("return void!\n"); }
    auto unhandled_exception() {}
};

auto Awaitable::await_resume() -> void {
    printf("await_resume!\n");
    start_ = promise->iterpair.first;
    end_ = promise->iterpair.second;
}

auto Parser::feed(std::convertible_to<std::string_view> auto&& view) {
    auto my_view = std::string_view{ view };
    promise().iterpair = { my_view.begin(), my_view.end() };
}


auto coro() -> Parser {

    std::string_view::iterator start, end;

    // feed1
    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };
    
    // feed2
    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };
    
    // feed3
    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };

    co_return;
}

int main() {

    std::string_view part1 = "Hello sir!";
    std::string_view part2 = "How is the world doing?";
    std::string_view part3 = "Blazingly good sir!";

    Parser myparser = coro();

    myparser.feed(part1);
    myparser.feed(part2);
    myparser.feed(part3);
}

段错误原因

1. 野指针访问直接触发崩溃

Awaitable::await_ready()返回true,根据C++协程规则,此时await_suspend()不会被执行,直接进入await_resume()。但Awaitable::promise指针仅在await_suspend()中被赋值,此时它是未初始化的野指针。在await_resume()中访问promise->iterpair时,野指针解引用直接触发段错误。

2. 协程执行逻辑完全偏离设计意图

你希望协程挂起等待外部feed()传入数据,但:

  • Promise::initial_suspend()返回std::suspend_never,导致协程创建后立即开始执行;
  • Awaitable::await_ready()返回true,导致每次co_await都不会挂起协程。

最终协程会一口气执行完所有三次co_await和co_return,完全没有等待外部输入的过程,同时触发野指针错误。

修复建议

  1. 修改Awaitable::await_ready()返回false,确保await_suspend()被调用,正确初始化promise指针;
  2. 修改Promise::initial_suspend()返回std::suspend_always,让协程创建后先挂起,等待第一次feed()调用时再唤醒;
  3. 在Parser::feed()中添加resume()调用,设置完数据后唤醒协程处理输入;
  4. 优化析构逻辑,避免销毁未完成的协程帧。

修复后的示例代码:

#include <cstdio>
#include <coroutine>
#include <string_view>

struct Promise;

struct Parser : public std::coroutine_handle<Promise> {
    using promise_type = ::Promise;

    auto feed(std::convertible_to<std::string_view> auto&& view);

    ~Parser() {
        if (done()) {
            destroy();
        }
    }
};

struct Awaitable {
    Promise* promise;
    std::string_view::iterator& start_;
    std::string_view::iterator& end_;

    Awaitable(std::string_view::iterator& start, std::string_view::iterator& end)
        : start_{ start }
        , end_{ end }
    { }
    auto await_ready() -> bool { printf("await_ready!\n"); return false; }
    auto await_suspend(std::coroutine_handle<Promise> handle) {
        promise = &handle.promise();
        printf("await_suspend!\n");
        return handle;
    }
    auto await_resume() -> void;
};

struct Promise {
    std::pair<std::string_view::iterator, std::string_view::iterator> iterpair;

    auto get_return_object() -> Parser { return {Parser::from_promise(*this)}; };
    auto initial_suspend() noexcept -> std::suspend_always { return {}; }
    auto final_suspend() noexcept -> std::suspend_always { return {}; }
    auto await_transform(std::pair<std::string_view::iterator, std::string_view::iterator> iterpair) -> Awaitable { return {iterpair.first, iterpair.second}; }
    auto return_void() { printf("return void!\n"); }
    auto unhandled_exception() {}
};

auto Awaitable::await_resume() -> void {
    printf("await_resume!\n");
    start_ = promise->iterpair.first;
    end_ = promise->iterpair.second;
    printf("Received data: %.*s\n", static_cast<int>(end_ - start_), &*start_);
}

auto Parser::feed(std::convertible_to<std::string_view> auto&& view) {
    auto my_view = std::string_view{ view };
    promise().iterpair = { my_view.begin(), my_view.end() };
    resume();
}

auto coro() -> Parser {
    std::string_view::iterator start, end;

    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };
    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };
    co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end };

    co_return;
}

int main() {
    std::string_view part1 = "Hello sir!";
    std::string_view part2 = "How is the world doing?";
    std::string_view part3 = "Blazingly good sir!";

    Parser myparser = coro();

    myparser.feed(part1);
    myparser.feed(part2);
    myparser.feed(part3);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:54:54