I'm working with C on Linux and I need to develop a common library to simplify the multi-threading development.
Well, I know that there are some mechanism of multi-threading in C 11, such as std::async, std::future etc. But I have to work with pthread because of some historical reason.
Basically, what I'm trying to do is to make a very very simple template function, which is kind of like std::future. Here it is.
template<typename S>
struct signature;
template<typename R, typename... Args>
struct signature<R(*)(Args...)> {
using return_type = R;
using argument_type = std::tuple<R(*)(Args...), Args...>; // f, args...
};
template<typename F, typename... Args>
void func(F* f, Args&&... args) {
typename signature<decltype(f)>::argument_type tp = std::make_tuple(f, std::forward<Args>(args)...);
pthread_t td;
pthread_create(&td, nullptr, [](void *p){
auto param = static_cast<typename signature<decltype(f)>::argument_type*>(p);
std::get<0>(*param)(std::get<1>(*param)); // ???
return (void*)nullptr;
}, &tp);
}
void f(int a) {}
void f2(int a, int b) {}
int main() {
func(f, 1);
// func(f2, 2, 2); ERROR!
return 0;
}
In a word, I try to wrap the parameters of the function into a tuple and pass the tuple into the third parameter of pthread_create, which is a labmda.
So in the piece of code, std::get<0>(*param) is the function, and the rest part of the tuple *param is the parameter list which should be passed to the function. But I don't know how to expand it. Obvisouly, std::get<0>(*param)(std::get<1>(*param)); is not OK because it can only handle the function with one parameter. If I want to pass a function with two parameters, I will get an error.
So how to expand the tuple there?
BTW, please ignore other issues, such as why don't call pthread_join. I just remove them here to minimize my post.
CodePudding user response:
As mentioned in the comments, std::apply is suitable for your case.
pthread_create(
&td, nullptr,
[](void* p) {
auto param = static_cast<typename signature<decltype(f)>::argument_type*>(p);
std::apply([](auto& f, auto&&... args) {
f(std::forward<decltype(args)>(args)...);
}, *param);
return (void*)nullptr;
},
&tp);
CodePudding user response:
If you can't use C 17 (std::apply()), I suppose you have to add a level of indirection...
Your func() can call a func2()
template<typename F, typename... Args>
void func(F* f, Args&&... args) {
func2(std::make_index_sequence<sizeof...(Args)>{}, f,
std::forward<Args>(args)...);
}
that receive also a template sequence of indexes, so your lambda, now inside func2() (I've removed the pthread part... sorry) can be written as follows
template <std::size_t ... Is, typename F, typename ... Args>
void func2 (std::index_sequence<Is...>, F * f, Args && ... args) {
typename signature<decltype(f)>::argument_type tp = std::make_tuple(f, std::forward<Args>(args)...);
[](void *p){
auto param = static_cast<typename signature<decltype(f)>::argument_type*>(p);
std::get<0>(*param)(std::get<1u Is>(*param)...);
return (void*)nullptr; }(&tp);
}
