目录
1.简介
continuable
是一个专注于 异步操作延续(continuation) 的现代 C++ 开源库,旨在简化异步编程流程,解决 "回调地狱" 问题,提供直观、灵活的异步操作链式调用和组合能力。它的设计理念类似于 JavaScript 中的 Promise
,但更贴合 C++ 语法特性和现代标准(C++11 及以上)。
异步编程中,多个依赖的异步操作(如网络请求、文件 IO)常导致嵌套回调("回调地狱"),代码可读性和维护性极差。continuable
通过 延续传递风格(Continuation-Passing Style) 封装异步操作,允许将异步操作的 "后续处理逻辑"(延续)通过链式调用串联,使异步流程更接近同步代码的线性结构。
主要特性有:
- 链式延续 :通过
then()
方法串联异步操作,前一个操作的结果自动传递给下一个延续。 - 错误处理 :统一的错误传播机制,通过
catch_()
捕获链中任意环节的错误。 - 操作组合 :支持并行(
when_all()
)、串行(when_seq()
)等方式组合多个异步操作。 - 多范式兼容 :可与回调函数、
std::future
、协程(C++20)等多种异步模型集成。 - 轻量级:header-only 库(仅需包含头文件),无外部依赖,易于集成。
- 类型安全:通过模板推导自动处理参数类型,编译期检查类型匹配。
2.安装与集成
continuable
是 header-only 库,无需编译,只需:
1.从 GitHub 仓库 下载源码或从网站下载压缩包。
2.将 include
目录添加到项目包含路径。
3.包含核心头文件 #include "continuable/continuable.hpp"
即可使用。
支持 C++11 及以上标准,兼容主流编译器(GCC、Clang、MSVC)。
注意:continuable依赖asio和function2库
asio库源码的下载地址:
function2的引用可参考:
3.快速入门
1.通过make_continuable
创建一个可延续对象,该对象在调用时会返回一个承诺:
cpp
auto http_request(std::string url) {
return cti::make_continuable<std::string>([url = std::move(url)](auto&& promise) {
// Perform the actual request through a different library,
// resolve the promise upon completion of the task.
promise.set_value("<html> ... </html>");
// or: promise.set_exception(std::make_exception_ptr(std::exception("Some error")));
// or: promise.set_canceled();
});
}
auto mysql_query(std::string query) {
return cti::make_continuable<result_set, bool>([url = std::move(url)](auto&& promise) {
// ^^^^^^^^^^^^^^ multiple result types
});
}
auto do_sth() {
return cti::make_continuable<void>([](auto&& promise) {
// ^^^^ no result at all
});
}
auto run_it() {
return async([] {
// Directly start with a handler
});
}
continuable<> run_it() { // With type erasure
return async([] {
});
}
2.通过then
附加您的延续,支持多个结果和部分处理程序:
cpp
mysql_query("SELECT `id`, `name` FROM `users`")
.then([](result_set users) {
// Return the next continuable to process ...
return mysql_query("SELECT `id` name FROM `sessions`");
})
.then([](result_set sessions) {
// ... or pass multiple values to the next callback using tuples or pairs ...
return std::make_tuple(std::move(sessions), true);
})
.then([](result_set sessions, bool is_ok) {
// ... or pass a single value to the next callback ...
return 10;
})
.then([](auto value) {
// ^^^^ Templated callbacks are possible too
})
// ... you may even pass continuables to the `then` method directly:
.then(mysql_query("SELECT * `statistics`"))
.then([](result_set result) {
// ...
return "Hi";
})
.then([] /*(std::string result) */ { // Handlers can accept a partial set of arguments{
// ...
});
3.通过fail
或next
处理故障:
cpp
http_request("example.com")
.then([] {
throw std::exception("Some error");
})
.fail([] (std::exception_ptr ptr) {
if (ptr) {
try {
std::rethrow_exception(ptr);
} catch(std::exception const& e) {
// Handle the exception or error code here
}
}
});
4.通过特定的执行器调度延续(可能在不同的线程上或稍后)
cpp
auto executor = [](auto&& work) {
// Dispatch the work here, store it for later invocation or move it to another thread.
std::forward<decltype(work)>(work)();
};
read_file("entries.csv")
.then([](Buffer buffer) {
// ...
}, executor);
// ^^^^^^^^
5.通过when_all
、when_any
或when_seq
连接可延续对象:
cpp
// `all` of connections:
(http_request("github.com") && http_request("example.com") && http_request("wikipedia.org"))
.then([](std::string github, std::string example, std::string wikipedia) {
// The callback is called with the response of github,
// example and wikipedia.
});
// `any` of connections:
(http_request("github.com") || http_request("example.com") || http_request("wikipedia.org"))
.then([](std::string github_or_example_or_wikipedia) {
// The callback is called with the first response of either github,
// example or wikipedia.
});
// `sequence` of connections:
(http_request("github.com") >> http_request("example.com") >> http_request("wikipedia.org"))
.then([](std::string github, std::string example, std::string wikipedia) {
// The requests are invoked sequentially
});
// Mixed logical connections:
(http_request("github.com") && (http_request("example.com") || http_request("wikipedia.org")))
.then([](std::string github, std::string example_or_wikipedia) {
// The callback is called with the response of github for sure
// and the second parameter represents the response of example or wikipedia.
});
// There are helper functions for connecting continuables:
auto all = cti::when_all(http_request("github.com"), http_request("example.com"));
auto any = cti::when_any(http_request("github.com"), http_request("example.com"));
auto seq = cti::when_seq(http_request("github.com"), http_request("example.com"));
6.通过result
处理多个结果变量,并通过recover
从故障中恢复:
cpp
make_exceptional_continuable<void>(std::make_exception_ptr(std::exception("Some error"))
.fail([] (std::exception_ptr ptr) {
return recover();
})
.then([] () -> result<> {
// We recovered from the failure and proceeding normally
// Will yield a default constructed exception type to signal cancellation
return cancel();
});
7.对现有代码进行"promisify"处理,或使用(asio)完成令牌集成:
cpp
// Promisification of your existing code that accepts callbacks
auto async_resolve(std::string host, std::string service) {
return cti::promisify<asio::ip::udp::resolver::iterator>::from(
[&](auto&&... args) {
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
},
std::move(host), std::move(service));
}
// (boost) asio completion token integration
asio::io_context io_context;
asio::steady_timer steady_timer(io_context);
steady_timer.expires_after(std::chrono::seconds(5));
steady_timer.async_wait(cti::use_continuable)
.then([] {
// Is called after 5s
});
4.完整示例
continuable
是一个 header-only 库(仅需包含头文件,无需编译库本身),因此使用 CMake 配置项目时,只需指定头文件路径和 C++ 标准即可。以下是详细的 CMake 配置步骤和示例:
1.假设项目结构如下(将 continuable
作为第三方库放在项目中):

2.CMakeLists.txt 配置
核心配置包括:指定 C++ 标准(需 C++11 及以上)、添加 continuable
的头文件路径。
cpp
# 最低CMake版本要求
cmake_minimum_required(VERSION 3.10)
# 项目名称和版本
project(continuable_demo VERSION 1.0)
# 指定C++标准(continuable需要C++11及以上)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) # 禁用编译器扩展
# 添加continuable的头文件路径
# 假设continuable放在third_party目录下
#target_include_directories(continuable_demo
# PRIVATE
# ${CMAKE_CURRENT_SOURCE_DIR}/third_party/continuable/include
#)
message(STATUS "continuable directories: ${CMAKE_CURRENT_SOURCE_DIR}")
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/third_party/include
${CMAKE_CURRENT_SOURCE_DIR}/third_party/include/dep/function2/include
)
# 添加源文件
add_executable(continuable_demo
src/main.cpp
)
3.示例代码(src/main.cpp)
编写一个简单的异步操作示例,验证配置是否正确:
cpp
#include <iostream>
#include <thread>
#include <chrono>
// 引入continuable头文件
#include "continuable/continuable.hpp"
// 模拟异步操作:1秒后返回一个整数
auto async_operation(int value) {
return cti::make_continuable<int>([value](auto&& promise) {
// 在新线程中执行异步操作
std::thread([value, promise = std::forward<decltype(promise)>(promise)]() mutable {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
promise.set_value(value * 2); // 异步操作结果
}).detach();
});
}
int main() {
std::cout << "开始异步操作..." << std::endl;
// 链式调用异步操作
async_operation(5)
.then([](int result) {
std::cout << "第一步结果: " << result << "(5*2)" << std::endl;
return async_operation(result); // 传递结果到下一个异步操作
})
.then([](int result) {
std::cout << "第二步结果: " << result << "(10*2)" << std::endl;
})
/*.catch_([](const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
})*/;
// 等待异步操作完成(实际项目中用事件循环替代)
std::this_thread::sleep_for(std::chrono::seconds(3));
return 0;
}
4.编译步骤
cpp
mkdir build && cd build
cmake .. # 生成Makefile
cmake --build . --config Debug # 编译项目
.\Debug\continuable_demo # 运行程序

5.关键说明
1)header-only 特性
continuable
无需编译为静态库或动态库,只需在 CMake 中通过 target_include_directories
指定其头文件路径即可。
2)C++ 标准:
必须指定 CMAKE_CXX_STANDARD 11
或更高(如 C++14、C++17),否则会因语法不兼容导致编译错误。
3)跨平台兼容性
上述配置兼容 Windows(MSVC)、Linux(GCC/Clang)、macOS,只需确保编译器支持 C++11 及以上标准。
6.完整源码下载地址
通过网盘分享的文件:continuableTest.zip
链接: https://pan.baidu.com/s/1MkrlvYChgIWr2boGnwD4_g?pwd=1234 提取码: 1234
5.优势与适用场景
- 简化异步流程:将嵌套回调转为线性链式调用,代码更易读、维护。
- 灵活组合:支持串行、并行等复杂异步工作流,满足多任务依赖场景。
- 现代 C++ 友好:充分利用模板、lambda、移动语义等特性,类型安全且高效。
适用于需要处理大量异步操作的场景:网络编程(如 HTTP 请求、RPC 调用)、文件 IO、异步数据库操作等。