I want to create a simple but generic callback with variadic parameters. I also need storing them, because callback will called later in time (from a different thread which uses the storage).
Here's what I have until now:
#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;
}
But I don't want to call callback in fileMgr_ReadWithCallback
, because it should be an async method, so I'd rather store the callback function and its parameters, and use it later once File Manager is able to perform the operation.
So the question is, what's the way to store ReadFileCallback
and ...T
into a struct maybe?
Or if there is better way doing this, let me know please.
I need the variadic arguments, as sometimes the extra param is just an int, but sometimes it can be 2 ints, or 1 string, etc, and I want to keep the FileManager generic.
I'm limited to C++11, but Boost is available.
First of all, relying on a global variable is not a really good idea, because it will give wrong results if you call readFile
while a previous read is still in progress. So you really should encapsulate that whole reading process in a class.
You need to use a lambda or std::bind
because the parameters of callback
are not known outside of the fileMgr_ReadWithCallback
function.
So you create a wrapping callback accepting a std::string
as a parameter. And use a lambda to capture the parameters, the lambda accepts a string as a parameter, and passes the string and the params
to the callback:
storage = [=](std::string s) {
callback(s, params...);
};
And that's how the code then could look like (but as I already said that's bad design)
#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;
}