6

So I wanted to challenge myself by writing a small threadpool in C++, and I wanted to try to mimic the easy to use way that std::thread work with, that you can just create a thread and as parameters send a function and parameters for that function, compared to something like pthreads which force you to have a void* as the only indata for the function.

So far I have been able to use templates and parameter packs to create a function that can take another function and parameters for it and execute it, but I can't find a way to store them so that I can execute them at a later time (when there is a free thread in the threadpool). I have tried using both std::function together with std::tuple, and std::bind, but since I don't know exactly what types I am dealing with I can't find a way to store the function and the parameters so that I can use them later on in another part of my code, since at that point I no longer know what types everything is of. Down below is some code I have been messing around with that might help show how I mean.

template<typename Function, typename... Arguments>
void TestFunction(Function func, Arguments... parameters)
{

std::function<std::result_of<Function(Arguments...)>::type(Arguments...)>* tempFunc;
tempFunc = new std::function<std::result_of<Function(Arguments...)>::type(Arguments...)>(func);
void* funcPtr = tempFunc;
std::tuple<Arguments...>* tempTuple;
tempTuple = new std::tuple<Arguments...>(parameters...);
void* tuplePtr = tempTuple;

//func(parameters...);
(Arguments...)>*)funcPtr, *(std::tuple<Arguments...>*)tuplePtr);

auto bindTest = std::bind(func, parameters...);
bindTest();
void* bindPtr = &bindTest;

}

int main()
{
TestFunction(std::printf, "%d, %d, %d\n", 3, 2, 1);

getchar();
return 0;
}

It might be that it's not possible to do what I want to do, and in that case I guess I'll just have to switch to an approach more like pthreads. But if anyone knows a work around I would be grateful.

Gamewolf3000
  • 63
  • 2
  • 8
  • Have you tried lambdas? It can store function call and params w/o any problem. – Michael Nastenko Apr 20 '17 at 01:11
  • @MichaelNastenko I have looked into them but the problem of how to store them in a general sense applies to them as well – Gamewolf3000 Apr 20 '17 at 22:48
  • You can use `std::packaged_task` inside lambda and it'll take care of storing parameters and returning value. – Michael Nastenko Apr 21 '17 at 01:02
  • @MichaelNastenko From looking at some documentation of std::packaged_task I don't think it will solve my problem. I don't have a problem getting my arguments passed into a function (or lambda for that) it is how I can then store any kind of function (any kind of parameter list and any type of return value) in a general way, and still be able to later use them. Right now I can create any kind of function as far as I know, and I can store them using a void pointer, but I don't have a way to later recreate the function in a callable state from that pointer. – Gamewolf3000 Apr 21 '17 at 08:56
  • Store it in simple `void(void)` lambda, as I said before. Then you can cast it to std::function and call it. – Michael Nastenko Apr 21 '17 at 09:41

1 Answers1

2

The key thing is that you can store the return type of std::bind in a std::function. Because std::bind returns an object that is callable. You should then be able to store the std::function instance depending on how you want to handle the return type.

template<typename Function, typename... Arguments>
void TestFunction(Function func, Arguments... parameters)
{
    using Ret = typename std::result_of<Function>::type;
    std::function<Ret()> val{std::bind(func, parameters...)};
}

If you do this when you first recive the function you no longer have to think about the arguments type, and only the return type. How you handle the return type will depend on the usecase of storing the function. One simple approach is to require that Function is a void function, which may make sense if there is no way to pass the value back to the consumer of the API.

user1937198
  • 4,987
  • 4
  • 20
  • 31
  • That sounds like a decent solution, I would like to be able to have return values but I could probably use your way and just have different ways to store the standard types (like int, float and char) and not let the user send in functions with custom type return values. I am guessing then that there is no way to store a completely general function then? – Gamewolf3000 Apr 19 '17 at 21:59
  • @Gamewolf3000 How are you planning on passing the return value back to consumers of TestFunction? If you plan on returning a future like value directly out of testfunction you might be able to convert the function to a void callable. Alternatively if you want to use another function on the class storing the function you might have to parameterise that on the return type. – user1937198 Apr 19 '17 at 22:31
  • I originally planned to return a kind of smart pointer from the function of the api from which information about the status of the task (waiting, done, etc) can be retrieved, as well as having a void pointer to the return data. But the more I think of it the more it falls apart, since even if I can generalize it, then there is the question how to deal with non dynamically allocated returns (who is responsible for it). So instead it's probably better if you can't get the return value and the user works around it, but it would still be nice to have a way to take any kind of function – Gamewolf3000 Apr 20 '17 at 22:47