2017-08-10 60 views
1

這是對a question I got answered yesterday的跟進。指向成員對象的指針 - 中斷線程

我試圖讓成員函數中斷線程,但我不知道是什麼,這些錯誤是:

[[email protected] projects]$ g++ -o test test.cpp -lpthread 
test.cpp: In instantiation of ‘InterruptibleThread::InterruptibleThread(Function&&, Args&& ...)::<lambda(std::atomic_bool&, std::atomic_bool&, auto:1&&, Args&& ...)> [with auto:1 = void (MyClass::*)(int); Function = void (MyClass::*)(int); Args = {MyClass*, int}; std::atomic_bool = std::atomic<bool>]’: 
/usr/local/include/c++/6.3.0/type_traits:2481:26: required by substitution of ‘template<class _Fn, class ... _Args> static std::__result_of_success<decltype (declval<_Fn>()((declval<_Args>)()...)), std::__invoke_other> std::__result_of_other_impl::_S_test(int) [with _Fn = InterruptibleThread::InterruptibleThread(Function&&, Args&& ...) [with Function = void (MyClass::*)(int); Args = {MyClass*, int}]::<lambda(std::atomic_bool&, std::atomic_bool&, auto:1&&, MyClass*&&, int&&)>; _Args = {std::reference_wrapper<std::atomic<bool> >, std::reference_wrapper<std::atomic<bool> >, void (MyClass::*)(int), MyClass*, int}]’ 
/usr/local/include/c++/6.3.0/type_traits:2492:55: required from ‘struct std::__result_of_impl<false, false, InterruptibleThread::InterruptibleThread(Function&&, Args&& ...) [with Function = void (MyClass::*)(int); Args = {MyClass*, int}]::<lambda(std::atomic_bool&, std::atomic_bool&, auto:1&&, MyClass*&&, int&&)>, std::reference_wrapper<std::atomic<bool> >, std::reference_wrapper<std::atomic<bool> >, void (MyClass::*)(int), MyClass*, int>’ 
/usr/local/include/c++/6.3.0/type_traits:2496:12: required from ‘class std::result_of<InterruptibleThread::InterruptibleThread(Function&&, Args&& ...) [with Function = void (MyClass::*)(int); Args = {MyClass*, int}]::<lambda(std::atomic_bool&, std::atomic_bool&, auto:1&&, MyClass*&&, int&&)>(std::reference_wrapper<std::atomic<bool> >, std::reference_wrapper<std::atomic<bool> >, void (MyClass::*)(int), MyClass*, int)>’ 
/usr/local/include/c++/6.3.0/functional:1365:61: required from ‘struct std::_Bind_simple<InterruptibleThread::InterruptibleThread(Function&&, Args&& ...) [with Function = void (MyClass::*)(int); Args = {MyClass*, int}]::<lambda(std::atomic_bool&, std::atomic_bool&, auto:1&&, MyClass*&&, int&&)>(std::reference_wrapper<std::atomic<bool> >, std::reference_wrapper<std::atomic<bool> >, void (MyClass::*)(int), MyClass*, int)>’ 
/usr/local/include/c++/6.3.0/thread:137:26: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = InterruptibleThread::InterruptibleThread(Function&&, Args&& ...) [with Function = void (MyClass::*)(int); Args = {MyClass*, int}]::<lambda(std::atomic_bool&, std::atomic_bool&, auto:1&&, MyClass*&&, int&&)>; _Args = {std::reference_wrapper<std::atomic<bool> >, std::reference_wrapper<std::atomic<bool> >, void (MyClass::*)(int), MyClass*, int}]’ 
test.cpp:41:33: required from ‘InterruptibleThread::InterruptibleThread(Function&&, Args&& ...) [with Function = void (MyClass::*)(int); Args = {MyClass*, int}]’ 
test.cpp:111:53: required from here 
test.cpp:36:9: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘fxn (...)’, e.g. ‘(... ->* fxn) (...)’ 
     fxn(forward<Args>(args)...); 

當前代碼:

using namespace std; 

class InterruptThreadException {}; 

class InterruptibleThread { 
private: 
    static thread_local atomic_bool* stopRef; 
    static thread_local atomic_bool* pauseRef; 
    atomic_bool stopFlag{false}; 
    atomic_bool pauseFlag{false}; 
    thread thrd; 

public: 
    friend void checkForInterrupt(); 
    template < typename Function, typename... Args > 
    InterruptibleThread(Function&& _fxn, Args&&... _args) 
     : thrd(
       [](atomic_bool& sr, atomic_bool& pr, auto&& fxn, Args&&... args) { 
        stopRef = &sr; 
        pauseRef = &pr; 
        fxn(forward<Args>(args)...); 
       }, 
       ref(stopFlag), 
       ref(pauseFlag), 
       forward<Function>(_fxn), 
       forward<Args>(_args)...) { 
     thrd.detach(); 
    } 
    bool stopping() const { 
     return stopFlag.load(); 
    } 

    void stop() { 
     stopFlag.store(true); 
    } 

    void pause() { 
     pauseFlag.store(true); 
     cout << "setting pause flag: " << pauseFlag.load() << endl; 
    } 

    void start() { 
     pauseFlag.store(false); 
    } 
}; 

void checkForInterrupt() { 
    cout << "Pause flag: " << InterruptibleThread::pauseRef->load() << endl; 
    cout << "Stop flag: " << InterruptibleThread::stopRef->load() << endl; 
    while (InterruptibleThread::pauseRef->load()) { 
     cout << "Paused\n"; 
     this_thread::sleep_for(chrono::seconds(1)); 
    } 
    if (!InterruptibleThread::stopRef->load()) { 
     return; 
    } 
    throw InterruptThreadException(); 
} 

thread_local atomic_bool* InterruptibleThread::stopRef = nullptr; 
thread_local atomic_bool* InterruptibleThread::pauseRef = nullptr; 

void doWork() { 
    int i = 0; 
    try { 
     while (true) { 
      cout << "Checking for interrupt: " << i++ << endl; 
      checkForInterrupt(); 
      this_thread::sleep_for(chrono::seconds(1)); 
     } 
    } catch (InterruptThreadException) { 
     cout << "Interrupted\n\n"; 
    } 
} 

class MyClass { 
private: 
    int myInt; 
    void setInt(int i) { 
     myInt = i; 
    } 

public: 
    MyClass() : myInt(1) { 
    } 
    void myWork(int i); 
    void doWork(); 
}; 

void MyClass::myWork(int i) { 
    setInt(i); 
    cout << "myInt value: " << myInt << endl; 
} 

void MyClass::doWork() { 
    InterruptibleThread t(&MyClass::myWork, this, 666); 
} 

int main() { 
    MyClass mc; 
    mc.doWork(); 

    cout << "Press enter to exit" << endl; 
    getchar(); 
    return 0; 
} 

我試過編譯器的建議,得到了之後的錯誤與fold表達式有關(afaik是C++ 17,我不會使用超過14的任何東西)。任何想法如何讓這個工作?

我有一個沒有使用lambdas的工作版本,但我真的好奇如何讓這與原始代碼一起工作。

+0

它在我看來像你的編譯器不滿意你的'類interruptiblethread'的定義中的'靜態'修飾符。你能把這些拿出來嗎?只是爲了看看它是否有幫助? –

+0

你是指靜態thread_locals?如果是這樣,thread_locals無論如何都是隱式靜態的,但某些編譯器不會明確地靜態聲明編譯(有人糾正我,如果我錯了,但我的不會) – user1324674

回答

2

指向成員函數的指針使用與指向非成員函數或其他函數的指針不同的語法來調用。由於您的案例中的Functionvoid (MyClass::*)(int),因此您需要使用語法(object.*fxn)(arg)(objectPtr->*fxn)(arg)來調用它。

如果你有訪問C++ 17層的功能,你可以使用std::invoke均勻撥打不同類型的可調用的:

InterruptibleThread(Function&& _fxn, Args&&... _args) 
    : thrd(
      [](atomic_bool& sr, atomic_bool& pr, auto&& fxn, auto&&... args) { 
       stopRef = &sr; 
       pauseRef = &pr; 
       std::invoke(std::move(fxn), std::move(args)...); 
      }, 
      //... 

注:我改變Args&&... argsauto&&... args因爲Args&&...可能會導致問題。

如果您沒有訪問C++ 17,你可以自己實現invoke相當容易:

template <typename Callable, 
      typename... Args, 
      typename = std::enable_if_t<!std::is_member_function_pointer<Callable>::value>> 
decltype(auto) invoke(Callable&& c, Args&&... args) { 
    return std::forward<Callable>(c)(std::forward<Args>(args)...); 
} 

template <typename T, 
      typename T2, 
      typename Ret, 
      typename... FuncArgs, 
      typename... Args, 
      typename = std::enable_if_t<!std::is_pointer<T2>::value>> 
decltype(auto) invoke(Ret (T::*c)(FuncArgs...), T2&& t, Args&&... args) { 
    return (std::forward<T>(t).*c)(std::forward<Args>(args)...); 
} 

template <typename T, 
      typename T2, 
      typename Ret, 
      typename... FuncArgs, 
      typename... Args> 
decltype(auto) invoke(Ret (T::*c)(FuncArgs...), T2* t, Args&&... args) { 
    return (t->*c)(std::forward<Args>(args)...); 
} 

這並不盡一切std::invoke做,但它的一切,你需要它。

+0

首先非常感謝你,這解決了一般問題。我想在這裏稍微擴展一點,因爲我正在使用線程的未來/承諾,並且最初在創建線程時調用了move(promise)。這個使用您的調用建議的新版本會導致在錯誤的時間調用該移動,如果將承諾作爲參數傳遞,最終會破壞程序。你會碰巧知道這個的原因嗎?如何在lambda中調用移動並不關心這個問題? – user1324674

+0

@ user1324674被編輯爲分離成員函數參數類型和提供的參數類型。當它們不完全相同時(即,如果一個函數採用'const T',但你提供了'T &&'),讓它們相同會導致推導出正確類型的問題。這可能會解決你的問題;如果沒有,你需要發佈你的實際代碼導致你的問題。 –

+0

這裏是我用來測試的代碼(類似於我的實際應用程序):https://gist.github.com/anonymous/7287c1a542963ccee32b147bb850b856第140行我打電話給InterruptibleThread並通過它承諾。通常在這裏調用線程時,我會調用移動。我修改了調用,但是它沒有解決問題(錯誤看起來相同) – user1324674