2017-08-10 3 views
1

Dies ist eine Folge zu a question I got answered yesterday.Zeiger auf Elementobjekt - Interrupt Thread

Ich versuche, einen Interrupt-Thread für Member-Funktionen zu machen, aber ich habe keine Ahnung, was diese Fehler sind:

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

Aktuelle Code:

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

ich den Compiler Vorschlag versucht und bekam ein Fehler im Nachhinein, der sich auf Faltexpression bezieht (die afaik sind C++ 17 und ich soll nichts außer 14 verwenden). Irgendeine Idee, wie man das zur Arbeit bringt?

Ich habe eine funktionierende Version von diesem ohne Lambdas zu verwenden, aber ich bin wirklich neugierig, wie man das mit dem ursprünglichen Code arbeiten lässt.

+0

Es sieht für mich so aus, als wäre Ihr Compiler nicht zufrieden mit den 'statischen' Modifikatoren in der Definition für Ihre 'Klasse interruptiblethread'. Kannst du die rausnehmen? Nur um zu sehen, ob es hilft? –

+0

Beziehen Sie sich auf die statischen thread_locals? Wenn ja, thread_locals sind implizit statisch, aber bestimmte Compiler kompilieren nicht ohne die statische Deklaration explizit (jemand korrigiert mich, wenn ich falsch liege, aber meins wird) – user1324674

Antwort

2

Zeiger auf Elementfunktionen werden mit einer anderen Syntax aufgerufen als Zeiger auf Nichtmitgliedsfunktionen oder andere Funktoren. Da Function in Ihrem Fall ein void (MyClass::*)(int) ist, müssen Sie es mit der Syntax (object.*fxn)(arg) oder (objectPtr->*fxn)(arg) aufrufen.

Wenn Sie haben Zugriff auf C++ 17-Funktionen können Sie std::invoke verwenden einheitlich verschiedene Arten von Callables zu nennen:

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)...); 
      }, 
      //... 

Anmerkung: Ich Args&&... args-auto&&... args seit Args&&... geändert zu Problemen führen könnte.

Wenn Sie keinen Zugriff haben C 17 ++, können Sie invoke sich ziemlich leicht implementieren:

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

Das nicht alles tun, was std::invoke tut, aber es ist alles, was Sie brauchen es für .

+0

Zunächst einmal vielen Dank, das hat das allgemeine Problem gelöst. Ich möchte das ein wenig erweitern, da ich Future/Versprechungen mit meinem Threading verwende und ursprünglich move (promise) beim Erstellen des Threads aufruft. Diese neue Version, die Ihren Aufrufvorschlag verwendet, bewirkt, dass die Bewegung zum falschen Zeitpunkt aufgerufen wird, wodurch das Programm schließlich unterbrochen wird, wenn eine Zusage als Argument übergeben wird. Würdest du zufällig den Grund dafür wissen? Wieso kümmert sich Calling move im Lambda nicht um dieses Problem? – user1324674

+0

@ user1324674 Bearbeitet, um die Elementfunktion arg types und die bereitgestellten arg types zu trennen. Wenn sie dieselben waren, führte dies dazu, dass die richtigen Typen abgeleitet wurden, wenn sie nicht genau gleich waren (d. H., Wenn eine Funktion ein "const T &" nahm, aber ein 'T &&'). Das könnte dein Problem lösen. Wenn nicht, müssen Sie Ihren tatsächlichen Code veröffentlichen, der Ihnen Probleme verursacht. –

+0

Hier ist der Code, den ich benutze, um zu testen (es ist ähnlich wie meine eigentliche App): https://gist.github.com/anonymous/7287c1a542963ccee32b147bb850b856 Zeile 140 Ich rufe InterruptibleThread und pass es ein Versprechen. Normalerweise würde ich beim Aufruf von Thread hier move aufrufen. Ich änderte die Aufrufe wie Sie haben, aber es hat das Problem nicht gelöst (der Fehler sieht gleich aus) – user1324674

Verwandte Themen