要在c++ 里面实现 Python yield 功能,于是问了豆包,豆包给了三段代码,分别使用 C++20 原生协程、Boost.Coroutine2 和手写生成器三种方式实现了 yield 功能。于是在 Mac 电脑上创建了一个目录 cyield,打开vscode 在 Github Copilot 的帮助下创建工程,编译运行以测试代码是否真实有效。以下是我与Github Copilot整个会话过程的记录。

会话过程

  • 提示词:创建一个CMake 项目,本项目由三个软件构成,分别为c20_yield,c17_yield, boost_yield, 对应C++20 版本, c++ 17 版本, boost 版本的程序。每个程序本身很简单,只有一个文件。

Copilot 回复:

文件已创建。下一步我将尝试构建项目以验证配置和可执行文件。

创建的目录结构如下:

cyield/
├─ CMakeLists.txt
├─ README.md
├─ c20_yield/
│  ├─ CMakeLists.txt
│  └─ c20_yield.cpp
├─ c17_yield/
│  ├─ CMakeLists.txt
│  └─ c17_yield.cpp
└─ boost_yield/
  ├─ CMakeLists.txt
  └─ boost_yield.cpp
  • 提示词:继续
文件已创建

- 根 CMake: CMakeLists.txt
- C++20 程序: c20_yield.cpp + CMakeLists.txt
- C++17 程序: c17_yield.cpp + CMakeLists.txt
- Boost 程序: boost_yield.cpp + CMakeLists.txt
- 说明: README.md
构建结果

. 已在 build 运行 cmake 并构建:C++20 与 C++17 可执行文件已成功生成到 bin,但 boost_yield 构建失败,原因是缺少 Boost 头文件(错误:'boost/version.hpp' file not found)。

接下来的可选操作

.如果你已安装 Homebrew,可用下面命令安装 Boost 然后重试构建:
brew install boost
cd /Users/bowyee/src/cyield
mkdir -p build && cd build
cmake ..
cmake --build . --parallel
. 或者我可以修改 boost_yield 使其不依赖 Boost(例如改为仅打印一条常量消息),如果你希望这样请告诉我。
  • 提示词:安装boost

一通操作后成功安装 Boost 并重新构建项目,boost_yield 也成功生成。

运行示例(在仓库根目录):

./build/bin/c20_yield
./build/bin/c17_yield
./build/bin/boost_yield

输出均符合预期。

为了日后进行比较,我先保存一版

  • 提示词:commit 一版:
cd /Users/bowyee/src/cyield && git status --porcelain --branch || true

git init && git add . && git commit -m "Initial project: add c20_yield, c17_yield, boost_yield, CMakeLists and README"

git rev-parse --short HEAD

git 提交成功. 注意,它没有生成.gitigore,我自己手动创建了 .gitigore

我将 豆包提供的 c20_yield.cpp 代码粘贴上,进行编译,报错。干脆让 Copilot 帮我解决:

  • 提示词:我们先从c20_yield 开始。我的本意是想在c++ 中实现 yield 机制。代码编译报错:/Users/bowyee/src/cyield/c20_yield/c20_yield.cpp:2:10: fatal error: ‘coroutine’ file not found #include

Copilot 回复:

你当前的代码使用了 C++20 协程(<coroutine>、std::coroutine_handle 等),但系统自带的 AppleClang(你的编译器)没有提供该头文件,所以报错。解决方案有三种,我推荐第一种:

选项 A(推荐)—— 安装新版 LLVM/Clang(via Homebrew),用它编译(支持 C++20 协程):

. 我可以为你安装 llvm 并用其 clang++ 重新配置并构建项目。
. 命令示例(我可以代你运行):
brew install llvm
cmake -S . -B build -DCMAKE_CXX_COMPILER="$(brew --prefix llvm)/bin/clang++" -DCMAKE_C_COMPILER="$(brew --prefix llvm)/bin/clang"
cmake --build build --parallel
选项 B(快速替代)—— 使用 Boost 的协程实现(例如 Boost.Coroutine2 / Boost.Context),把代码改为基于 Boost 的生成器(无需语言协程)。优点:可以在当前编译器上构建;缺点:依赖 Boost API,语义与 co_yield 不同。
选项 C(最简单)—— 不用语言协程,自己实现状态机/迭代器风格的生成器(移除 <coroutine>),这是兼容性最强但最繁琐的实现方式。
  • 提示词:选项ABC 我都想要。

于是神奇的一幕发生了,Copilot 实现了 Boost 版本的 boost_yield.cpp,问我是否继续。

  • 提示词:继续

又实现了手写生成器版本的 c17_yield.cpp.

然后再次问我是否要安装 llvm 并用其 clang++ 重新配置并构建 c20_yield。

  • 提示词:安装 llvm 吧

Copilot 又是一通操作,中间还出了点儿问题,屏幕上连续输出无数个 1 也不停。我按 Ctrl+C 停止了它。

Copilot 也没问我,也没解释,接着干活儿,最后安装成功了。才问我

要我现在检查并修复 c20_yield 的协程实现吗?
  • 提示词:修复

Copilot 自己又是一通操作,终于成功了。

  • 提示词:此程序还要在Windows 与 Linux 上进行编译测试,请在Readme.md 中对在Mac 上编译时需要安装llvm 进行说明。并修改CMakeLists.txt

全部更新完成。

最终代码

以下是最终生成的文件:

根 CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(cyield LANGUAGES CXX)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
	if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.0")
		message(WARNING "Detected AppleClang ${CMAKE_CXX_COMPILER_VERSION}. This compiler may lack full C++20 coroutine support (\"<coroutine>\").\n  To build c20_yield enable a newer Clang/LLVM (recommended via Homebrew):\n    brew install llvm\n    cmake -S . -B build-llvm -DCMAKE_C_COMPILER=\"$(brew --prefix llvm)/bin/clang\" -DCMAKE_CXX_COMPILER=\"$(brew --prefix llvm)/bin/clang++\"\n    cmake --build build-llvm --parallel")
	endif()
endif()

add_subdirectory(c20_yield)
add_subdirectory(c17_yield)
add_subdirectory(boost_yield)

C++20 版本

c20_yield/CMakeLists.txt

add_executable(c20_yield c20_yield.cpp)
target_compile_features(c20_yield PUBLIC cxx_std_20)
set_target_properties(c20_yield PROPERTIES CXX_EXTENSIONS OFF)

c20_yield/c20_yield.cpp

#include <iostream>
#include <coroutine>

template <typename T>
struct Generator {
    struct promise_type {
        T current_value;
        std::suspend_always yield_value(T value) noexcept {
            current_value = value;
            return {};
        }
        std::suspend_always initial_suspend() noexcept { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        Generator get_return_object() noexcept {
            return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        void unhandled_exception() { std::terminate(); }
        void return_void() {}
    };

    std::coroutine_handle<promise_type> handle;
    explicit Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
    Generator(Generator&& other) noexcept : handle(other.handle) { other.handle = nullptr; }
    ~Generator() { if (handle) handle.destroy(); }

    bool move_next() {
        if (!handle || handle.done()) return false;
        handle.resume();
        return !handle.done();
    }

    T current() const { return handle.promise().current_value; }
};

Generator<int> range(int start, int end) {
    for (int i = start; i < end; ++i) {
        co_yield i;
    }
    co_return;
}

int main() {
    auto gen = range(1, 5);
    while (gen.move_next()) {
        std::cout << gen.current() << " ";
    }
    std::cout << "\n";
    return 0;
}

运行结果:

% ./c20_yield 
1 2 3 4

C++ 17 版本:

c17_yield/CMakeLists.txt

add_executable(c17_yield c17_yield.cpp)
target_compile_features(c17_yield PUBLIC cxx_std_17)
set_target_properties(c17_yield PROPERTIES CXX_EXTENSIONS OFF)

c17_yield/c17_yield.cpp

#include <iostream>
#include <optional>

// 简单的手写生成器(状态机风格),模拟 yield 行为
class Generator {
public:
    Generator(int start, int end) : cur(start), end(end) {}

    // move to next value; return true if there's a value
    bool move_next() {
        if (cur < end) {
            value = cur++;
            return true;
        }
        value.reset();
        return false;
    }

    int current() const { return *value; }

private:
    int cur;
    int end;
    std::optional<int> value;
};

int main() {
    Generator gen(1, 5);
    while (gen.move_next()) {
        std::cout << gen.current() << " ";
    }
    std::cout << "\n";
    return 0;
}

运行结果:

% ./c17_yield 
1 2 3 4 

Boost 版本:

boost_yield/CMakeLists.txt

add_executable(boost_yield boost_yield.cpp)
target_compile_features(boost_yield PUBLIC cxx_std_17)
set_target_properties(boost_yield PROPERTIES CXX_EXTENSIONS OFF)

find_package(Boost REQUIRED COMPONENTS coroutine context)
if(Boost_FOUND)
  target_include_directories(boost_yield PRIVATE ${Boost_INCLUDE_DIRS})
  target_link_libraries(boost_yield PRIVATE ${Boost_LIBRARIES})
else()
  message(FATAL_ERROR "Boost (coroutine, context) not found — install Boost to build boost_yield.")
endif()

boost_yield/boost_yield.cpp

#include <iostream>
#include <boost/version.hpp>
#include <boost/coroutine2/coroutine.hpp>

using coro_t = boost::coroutines2::coroutine<int>;

void generator(coro_t::push_type &yield) {
    for (int i = 1; i <= 4; ++i) {
        yield(i);
    }
}

int main() {
    std::cout << "boost_yield: BOOST_LIB_VERSION = " << BOOST_LIB_VERSION << "\n";
    coro_t::pull_type source(generator);
    while (source) {
        std::cout << source.get() << " ";
        source();
    }
    std::cout << "\n";
    return 0;
}

运行结果

% ./boost_yield 
boost_yield: BOOST_LIB_VERSION = 1_90
1 2 3 4 

以上代码均在MacOS 与 Windows 环境下测试通过。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐