存储可变参数和回调以供以后使用

Storing variadic parameter and callback for later usage

我想创建一个带有可变参数的简单但通用的回调。我还需要存储它们,因为稍后会调用回调(从使用存储的不同线程)。

这是我到目前为止所拥有的:

#include <iostream>
#include <string>

using namespace std;

void fileMgr_DoneWithOtherstuff_DoTheReads() {
  FROM SOMESTORAGE get the callback and params
  Do the reading
  string result = "CONTENT";
  Call the callback with result string and params
}

template<class ReadFileCallback, class ...T>
void fileMgr_ReadWithCallback(string filename, ReadFileCallback callback, T ...params) {
    cout << "fileMgr_ReadWithCallback is processing file: " << filename << endl;
//        string content = "CONTENT";
//        callback(content, params...);

      SOMESTORAGE = callback + params;
}


void readFileResult(string contents, int id) {
    cout << "readFileResult callback: contents=" << contents << ", id=" << id << endl;
}

void readFile(string filename, int id) {
    fileMgr_ReadWithCallback(filename, readFileResult, id);
}

int main()
{
    int fileId = 1;
    readFile("myfile", fileId);
    
    return 0;
}

但我不想在fileMgr_ReadWithCallback中调用回调,因为它应该是一个异步方法,所以我宁愿存储回调函数及其参数,稍后在文件管理器中使用它能够执行操作。

所以问题是,将 ReadFileCallback...T 存储到结构中的方法是什么?

或者如果有更好的方法,请告诉我。

我需要可变参数,因为有时额外的参数只是一个整数,但有时它可以是 2 个整数或 1 个字符串等,我想保持 FileManager 通用。

我仅限于 C++11,但可以使用 Boost。

您可以通过 auto storedParams = std::make_tuple(params...) 将参数存储在元组中。稍后您可以简单地使用 std::apply(callback, storedParams ).

调用您的回调函数

要添加附加参数(如内容),您可以使用 std::tuple_cat(std::make_tuple(content), storedParams )

我会分别存储参数和回调。

顺便说一句:std::apply 仅适用于 C++17 - 有什么可以模拟的吗(也许在 boost 中)?否则我会尝试使用 cppreference 上显示的 std::invoke 和 std::apply 的实现(只需删除 constexpr...)。

在这里你可以找到代码:https://godbolt.org/z/z9MP3M

但是你需要添加std::apply(这是最难的部分)。为什么你需要使用这么老的编译器?


更新

也许您可以使用 std::function 的类型擦除功能,请参阅 here:

#include <iostream>
#include <string>
#include <functional>


static std::function<void(std::string)> fileManagerStorage;

void fileMgr_DoneWithOtherstuff_DoTheReads() {
  //FROM SOMESTORAGE get the callback and params
  
  //Do the reading
  std::string content = "CONTENT";
  fileManagerStorage(content);

  //Call the callback with result string and params
  fileManagerStorage(content);
}

template<class ReadFileCallback, class ...T>
void fileMgr_ReadWithCallback(std::string filename, ReadFileCallback callback, T ...params) {
    std::cout << "fileMgr_ReadWithCallback is processing file: " << filename << std::endl;
//        string content = "CONTENT";
//        callback(content, params...);

    fileManagerStorage = [=](std::string content){
        callback(content, params...);
    };
}


void readFileResult(std::string contents, int id) {
    std::cout << "readFileResult callback: contents=" << contents << ", id=" << id << std::endl;
}

void readFile(std::string filename, int id) {
    fileMgr_ReadWithCallback(filename, readFileResult, id);
}

int main()
{
    int fileId = 1;
    readFile("myfile", fileId);
    
    return 0;
}

(您不应将存储存储为全局变量 - 将其存储在您的线程中...)

首先,依赖全局变量并不是一个好主意,因为如果在之前的读取仍在进行时调用 readFile 会给出错误的结果。所以你真的应该将整个阅读过程封装在 class.

您需要使用 lambda 或 std::bind,因为 callback 的参数在 fileMgr_ReadWithCallback 函数之外是未知的。

因此您创建了一个包装回调,接受 std::string 作为参数。并使用 lambda 捕获参数,lambda 接受一个字符串作为参数,并将字符串和 params 传递给回调:

storage = [=](std::string s) {
   callback(s, params...);
};

这就是代码的样子(但正如我已经说过的那样,这是糟糕的设计)

#include <functional>
#include <iostream>


std::function<void(std::string)> storage;


void fileMgr_DoneWithOtherstuff_DoTheReads() {
  std::string result = "CONTENT";
  storage(result);
}

template<class ReadFileCallback, class ...T>
void fileMgr_ReadWithCallback(std::string filename, ReadFileCallback callback, T ...params) {
    std::cout << "fileMgr_ReadWithCallback is processing file: " << filename << std::endl;

    storage = [=](std::string s) {
        callback(s, params...);
    };
}

void readFileResult(std::string contents, int id) {
    std::cout << "readFileResult callback: contents=" << contents << ", id=" << id << std::endl;
}

void readFile(std::string filename, int id) {
    fileMgr_ReadWithCallback(filename, readFileResult, id);
}

int main()
{
    int fileId = 1;
    readFile("myfile", fileId);
    fileMgr_DoneWithOtherstuff_DoTheReads();
    
    return 0;
}