Github Copilot 实战:用三种方法让 C++ 实现 Python yield
摘要:本文记录了一个在C++中实现Python yield功能的实验过程。作者通过Github Copilot的帮助创建了三个版本的项目:使用C++20原生协程的c20_yield、使用C++17手写生成器的c17_yield,以及基于Boost.Coroutine2的boost_yield。
要在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 环境下测试通过。
更多推荐



所有评论(0)