Last active
October 14, 2019 05:02
-
-
Save johnb003/dbc4a69af8ea8f4771666ce2e383047d to your computer and use it in GitHub Desktop.
Revisions
-
johnb003 revised this gist
Oct 14, 2019 . 1 changed file with 15 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,21 +5,28 @@ template <typename... FuncArgs> class Signal { using Function = std::function<void(FuncArgs...)>; std::forward_list<std::weak_ptr<Function> > registeredListeners; public: using Listener = std::shared_ptr<Function>; Listener add(Function cb) { // cb was passed by address. When creating the shared ptr, we copy it by value // cb is often an r value lambda so, we need to make sure we copy it. auto const result = std::make_shared<Function>(std::move(cb)); registeredListeners.push_front(result); return result; } // Don't be fooled by FuncArgs&& (It's not a r-value reference) // The redundant template specification actually changes this for perfect forwarding. // See: https://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c template <typename... FuncArgs> void raise(FuncArgs&&... args) { registeredListeners.remove_if([&args...](const std::weak_ptr<Function> &e) { if (auto f = e.lock()) { (*f)(std::forward<FuncArgs>(args)...); return false; } return true; -
johnb003 created this gist
Oct 10, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,42 @@ #include <forward_list> #include <functional> #include <memory> template <typename... FuncArgs> class Signal { using fp = std::function<void(FuncArgs...)>; std::forward_list<std::weak_ptr<fp> > registeredListeners; public: using Listener = std::shared_ptr<fp>; Listener add(const std::function<void(FuncArgs...)> &cb) { Listener result(std::make_shared<fp>(cb)); registeredListeners.push_front(result); return result; } void raise(FuncArgs... args) { registeredListeners.remove_if([&args...](std::weak_ptr<fp> e) -> bool { if (auto f = e.lock()) { (*f)(args...); return false; } return true; }); } }; // To Use: // Store some signal provider // Signal<int> eventThing; // ... // Register a callback for the signal, and store the listener owning reference: // Signal<int>::Listener listener = eventThing.add([](int) { ... }); // when `listener` goes out of scope, the callback will not get called anymore. // Likewise, if you change `listener`, such as to add it to a different Signal<int>, // it will automatically remove it from the original Signal. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,86 @@ // Producer Example class A { public: int sourceVal; A(int i) : sourceVal(i) {} void Modify(int val) { sourceVal = val; bloopChanged.raise(val); } Signal<int> bloopChanged; }; // Consumer Example class B { public: // Like a "slot" // You can assign the slot to the signal, and it handles disconnect when reset(), or assigned to a different signal. Signal<int>::Listener bloopResponse; int x; B() { x = 0; } ~B() { x = -99; } void set(int a) { x = a; } }; void test() { A a1(1); A a2(2); A a3(3); // B scope { B b; auto cb = [&](int v) { b.set(v); }; b.bloopResponse = a1.bloopChanged.add(cb); printf("As: (%d, %d, %d) B: %d\n", a1.sourceVal, a2.sourceVal, a3.sourceVal, b.x); a1.Modify(10); a2.Modify(20); a3.Modify(30); printf("As: (%d, %d, %d) B: %d\n", a1.sourceVal, a2.sourceVal, a3.sourceVal, b.x); b.bloopResponse = a2.bloopChanged.add(cb); a1.Modify(11); a2.Modify(22); a3.Modify(33); printf("As: (%d, %d, %d) B: %d\n", a1.sourceVal, a2.sourceVal, a3.sourceVal, b.x); } a1.Modify(100); a2.Modify(200); a3.Modify(300); printf("As: (%d, %d, %d) B: (gone)\n", a1.sourceVal, a2.sourceVal, a3.sourceVal); } // Prints: // As: (1, 2, 3) B: 0 // As: (10, 20, 30) B: 10 // As: (11, 22, 33) B: 22 // As: (100, 200, 300) B: (gone) // Put a breakpoint in the labda, and you will see: // It only receives: 10, and 22, (it is not fired after leaving "B scope" // CAUTION: // This cannot protect against capturing an object in the lambda that goes out of scope. // If you need to, pass a weak_ptr in your lambda and check it: // [weak_ptr<YourObj> obj = your_shared_ptr_instance, other_captures]() { // if (auto o = obj.lock()) { // // stuff with o // // ... // } // });