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

如何定义支持任意返回值与参数的通用回调函数?

问题:定义支持任意返回类型、任意参数的回调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);

额外注意事项

  1. 示例代码的几个小错误需修正:
    • std::Cout改为小写std::cout
    • 输出registration_time时需要解引用*registration_time
    • 原调用SetCallback末尾缺少分号
    • 必须包含<functional>头文件才能使用std::bind
  2. 使用detach()分离线程时,要注意程序退出时线程可能未执行完毕的问题,实际项目可考虑用线程池管理线程生命周期

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 01:05:19