简体   繁体   中英

How to extract tuple into a function parameters

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.

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);

Demo.

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM