如何定义支持任意返回值与参数的通用回调函数?
问题:定义支持任意返回类型、任意参数的回调API
我正在开发一款API,希望允许应用开发者传入任意返回类型、任意参数数量的自定义回调函数。以下是预期可运行的示例代码,需要确定MyAPI.h中<??>处的userfunc类型定义:
待完善的MyAPI.h
namespace API { /** * Executes callback function after a random time in its own thread. * @param userfunc User defined callback function. **/ void SetCallback(<??> userfunc); }
示例应用代码application.cpp
#include "MyAPI.h" #include <chrono> #include <format> #include <iostream> #include <functional> #include <windows.h> std::string get_current_datetime() { const auto now = std::chrono::system_clock::now(); return std::format("{:%d-%m-%Y %H:%M:%OS}", now); } bool callback_1(bool* triggered, std::string* registration_time) { *triggered = true; std::cout << "CALLBACK_1" << std::endl; std::cout << "registered: " << *registration_time << std::endl; std::cout << "triggered : " << get_current_datetime() << std::endl; return true; } int callback_2(int* triggered) { *triggered = 1; std::cout << "CALLBACK_2" << std::endl; return 1; } int main(void) { bool is_triggered_bool = false; int is_triggered_int = 0; std::string curr = get_current_datetime(); API::SetCallback(std::bind(&callback_1, &is_triggered_bool, &curr)); API::SetCallback(std::bind(&callback_2, &is_triggered_int)); Sleep(3000); if (is_triggered_bool && is_triggered_int == 1) return 0; else return -1; }
解决方案
结合你提供的std::bind使用场景,有两种实用的实现方式:
方式1:用std::function<void()>适配绑定后的回调
因为std::bind会把带参函数包装成无参可调用对象,且你的API不需要处理回调返回值,直接用std::function<void()>作为参数类型最适配:
完善后的MyAPI.h
#include <functional> #include <thread> #include <random> #include <chrono> namespace API { void SetCallback(std::function<void()> userfunc) { // 启动后台线程执行回调 std::thread([func = std::move(userfunc)]() { // 生成100ms-2900ms的随机等待时间 std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> dist(100, 2900); std::this_thread::sleep_for(std::chrono::milliseconds(dist(gen))); // 执行回调 func(); }).detach(); } }
这种方式优势:
- 直接兼容
std::bind、lambda表达式等所有无参可调用对象 - API实现简洁,无需处理复杂模板逻辑
- 完全匹配你的示例调用方式
方式2:用模板函数支持直接传原函数+参数
如果想让开发者不用手动std::bind,直接传入函数和对应参数,可以用模板函数实现:
完善后的MyAPI.h
#include <thread> #include <random> #include <chrono> #include <utility> namespace API { template<typename Func, typename... Args> void SetCallback(Func&& func, Args&&... args) { std::thread([func = std::forward<Func>(func), args = std::make_tuple(std::forward<Args>(args)...)]() { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> dist(100, 2900); std::this_thread::sleep_for(std::chrono::milliseconds(dist(gen))); // 展开参数并执行回调 std::apply(func, args); }).detach(); } }
此时开发者的调用代码可以简化为:
// 无需std::bind,直接传函数+参数 API::SetCallback(callback_1, &is_triggered_bool, &curr); API::SetCallback(callback_2, &is_triggered_int);
额外注意事项
- 示例代码的几个小错误需修正:
std::Cout改为小写std::cout- 输出
registration_time时需要解引用*registration_time - 原调用
SetCallback末尾缺少分号 - 必须包含
<functional>头文件才能使用std::bind
- 使用
detach()分离线程时,要注意程序退出时线程可能未执行完毕的问题,实际项目可考虑用线程池管理线程生命周期
内容的提问来源于stack exchange,提问作者wsys
相关产品推荐
相关产品推荐

