2017-05-16 14 views
0

我想要子類std::thread,以便在調用者的傳入函數之前在新線程上執行子類的成員函數。類似下面的無效代碼:子類化std :: thread:在構造函數中使用可變參數模板函數的問題

#include <thread> 
#include <utility> 

class MyThread : public std::thread { 
    template<class Func, class... Args> 
    void start(Func&& func, Args&&... args) { 
     ... // Useful, thread-specific action 
     func(args...); 
    } 
public: 
    template<class Func, class... Args> 
    MyThread(Func&& func, Args&&... args) 
     : std::thread{[=]{start(std::forward<Func>(func), 
       std::forward<Args>(args)...);}} { 
    } 
}; 

g++ -std=c++11有以下問題與上面的代碼:

MyThread.h: In lambda function: 
MyThread.h:ii:jj: error: parameter packs not expanded with '...': 
        std::forward<Args>(args)...);}} 
            ^

我試過在初始化列表十幾種不同的變化都無濟於事。

我該怎麼做我想要的?

+0

在lambda中通過價值獲取一切並不是你想要做的。刪除'=',讓編譯器告訴你需要捕獲什麼,並考慮如何捕獲這些。 – nwp

+0

@nwp似乎沒有任何區別:仍然不會編譯。 –

+0

評論前的'...'無效'C++' – Galik

回答

1

我以前做這件事時遇到的最大困難是得到std::thread的所有行爲。它的構造函數不僅可以使用指向自由函數的指針,還可以使用類方法指針,然後將類的對象作爲第一個參數。有許多在該變型的:類方法,類函數對象數據成員,類型VS的指針的對象的對象等

這是C++ 14:

class MyThread : public std::thread { 
    void prolog() const { std::cout << "prolog\n"; } 

public: 
    template <typename... ArgTypes> 
    MyThread(ArgTypes&&... args) : 
     std::thread(
      [this, bfunc = std::bind(std::forward<ArgTypes>(args)...)] 
      () mutable { 
       prolog(); 
       bfunc(); 
      }) 
    { } 
}; 

如果您將序言碼放在lambda表達式中,並且它不調用類方法,則不需要捕獲this

對於C++ 11,需要一個小的變化,因爲缺乏捕獲初始化的,所以綁定必須作爲參數傳遞給std::thread

std::thread(
    [this] 
    (decltype(std::bind(std::forward<ArgTypes>(args)...))&& bfunc) mutable { 
     prolog(); 
     bfunc(); 
    }, std::bind(std::forward<ArgTypes>(args)...)) 

這是一個測試程序,也行使的std::thread類成員的形式:

int main() 
{ 
    auto x = MyThread([](){ std::cout << "lambda\n"; }); 
    x.join(); 

    struct mystruct { 
     void func() { std::cout << "mystruct::func\n"; } 
    } obj; 
    auto y = MyThread(&mystruct::func, &obj); 
    y.join(); 

    return 0; 
} 

我沒有檢查,但我有點擔心的this捕獲,也可見於其他的解決方案,是不是在某些情況下是安全的。考慮何時該對象是一個移動的右值,如std::thread t = MyThread(args)。我認爲MyThread對象在它創建的線程必須完成使用之前就會消失。 「線程」將被移動到一個新對象中,並且仍在運行,但捕獲的這個指針將指向一個現在過時的對象。

我認爲您需要確保您的構造函數不會返回,直到您的新線程完成使用所有引用或指向類或類成員的指針。如果可能的話,通過價值獲取會有所幫助。或者也許prolog()可能是一個靜態類方法。

+0

我明白你對捕獲'this'的關注。說實話,標準的C++方法是將線程看作是一個實現細節(即使它成爲一個impl的成員變量,而不是從它派生出來)。 MFC的方式是從線程對象派生出來的,但是事實證明這是有問題的(正如你毫無疑問經歷的那樣)。 –

1

這應該這樣做(C++ 11和提供的C++ 14種溶液):

C++ 14

#include <thread> 
#include <utility> 
#include <tuple> 

class MyThread : public std::thread { 

    template<class Func, class ArgTuple, std::size_t...Is> 
    void start(Func&& func, ArgTuple&& args, std::index_sequence<Is...>) { 
     // Useful, thread-specific action 
     func(std::get<Is>(std::forward<ArgTuple>(args))...); 
    } 
public: 
    template<class Func, class... Args> 
    MyThread(Func&& func, Args&&... args) 
     : std::thread 
     { 
      [this, 
      func = std::forward<Func>(func), 
      args = std::make_tuple(std::forward<Args>(args)...)]() mutable 
      { 
       using tuple_type = std::decay_t<decltype(args)>; 
       constexpr auto size = std::tuple_size<tuple_type>::value; 
       this->start(func, std::move(args), std::make_index_sequence<size>()); 
      } 
     } 
    { 
    } 
}; 

int main() 
{ 
    auto x = MyThread([]{}); 
} 

在C++ 17是微不足道:

#include <thread> 
#include <utility> 
#include <tuple> 
#include <iostream> 

class MyThread : public std::thread { 

public: 
    template<class Func, class... Args> 
    MyThread(Func&& func, Args&&... args) 
     : std::thread 
     { 
      [this, 
      func = std::forward<Func>(func), 
      args = std::make_tuple(std::forward<Args>(args)...)]() mutable 
      { 
       std::cout << "execute prolog here" << std::endl; 

       std::apply(func, std::move(args)); 

       std::cout << "execute epilogue here" << std::endl; 
      } 
     } 
    { 
    } 
}; 

int main() 
{ 
    auto x = MyThread([](int i){ 
     std::cout << i << std::endl; 
    }, 6); 
    x.join(); 
} 

C++ 11(我們必須促進將對象移動到可變的lambda中,並提供缺少的std :: index_sequence):

#include <thread> 
#include <utility> 
#include <tuple> 

namespace notstd 
{ 
    using namespace std; 

    template<class T, T... Ints> struct integer_sequence 
    {}; 

    template<class S> struct next_integer_sequence; 

    template<class T, T... Ints> struct next_integer_sequence<integer_sequence<T, Ints...>> 
    { 
     using type = integer_sequence<T, Ints..., sizeof...(Ints)>; 
    }; 

    template<class T, T I, T N> struct make_int_seq_impl; 

    template<class T, T N> 
     using make_integer_sequence = typename make_int_seq_impl<T, 0, N>::type; 

    template<class T, T I, T N> struct make_int_seq_impl 
    { 
     using type = typename next_integer_sequence< 
      typename make_int_seq_impl<T, I+1, N>::type>::type; 
    }; 

    template<class T, T N> struct make_int_seq_impl<T, N, N> 
    { 
     using type = integer_sequence<T>; 
    }; 

    template<std::size_t... Ints> 
     using index_sequence = integer_sequence<std::size_t, Ints...>; 

    template<std::size_t N> 
     using make_index_sequence = make_integer_sequence<std::size_t, N>; 
} 

template<class T> 
struct mover 
{ 
    mover(T const& value) : value_(value) {} 
    mover(T&& value) : value_(std::move(value)) {} 
    mover(const mover& other) : value_(std::move(other.value_)) {} 

    T& get() & { return value_; } 
    T&& get() && { return std::move(value_); } 

    mutable T value_; 
}; 

class MyThread : public std::thread { 

    template<class Func, class ArgTuple, std::size_t...Is> 
    void start(Func&& func, ArgTuple&& args, notstd::index_sequence<Is...>) { 
     // Useful, thread-specific action 
     func(std::get<Is>(std::forward<ArgTuple>(args))...); 
    } 
public: 
    template<class Func, class... Args> 
    MyThread(Func&& func, Args&&... args) 
     : std::thread() 
    { 
     using func_type = typename std::decay<decltype(func)>::type; 
     auto mfunc = mover<func_type>(std::forward<Func>(func)); 

     using arg_type = decltype(std::make_tuple(std::forward<Args>(args)...)); 
     auto margs = mover<arg_type>(std::make_tuple(std::forward<Args>(args)...)); 

     static_cast<std::thread&>(*this) = std::thread([this, mfunc, margs]() mutable 
     { 
       using tuple_type = typename std::remove_reference<decltype(margs.get())>::type; 
       constexpr auto size = std::tuple_size<tuple_type>::value; 
       this->start(mfunc.get(), std::move(margs).get(), notstd::make_index_sequence<size>()); 
     }); 

    } 
}; 

int main() 
{ 
    auto x = MyThread([](int i){}, 6); 
    x.join(); 
} 
+0

@nwp固定。非常感謝您注意到這一點。 –

+0

認真??沒有簡單的C++ 11解決方案? –

+0

@SteveEmmerson這裏大部分的併發症是填充缺少的std :: index_sequence。 –

相關問題