Boost.Signals2 - if slot (functor of lambda) destructs the signal, calling the slot itself -> safe for functor?
Hi there, If I connect a signal to a slot (functor or lambda), and the signal is then run, it calls the slot. What if the slot now has code that causes the signal to destruct? Will this "self-destruct" the slot (functor or lambda), by calling it's destructor BEFORE the slot-call has finished executing??? This would be a disaster! But it seems like is is safe. It seems: Even if the signal is destructed (from it's own slot-call), the slot destructor does not yet get called, until the slot-call has finished. Which would be safe. (Code example below) But is this really the case? Is this always safe? (If so: how does this roughly work?) Thanks. ///////////////////////////////////////////////////////////////////// #include <iostream> #include <string> #include <boost/signals2.hpp> #include <functional> #include <map> using namespace std; using FuncSig = void(); class BaseWrapper { public: BaseWrapper() { cout << "BaseWrapper() " << ++mm[""] << endl; } BaseWrapper(const string &n) : name{n} { cout << "BaseWrapper() " << name << ' ' << ++mm[name] << endl; } BaseWrapper(const BaseWrapper &other) : name{other.name} { cout << "BaseWrapper(const BaseWrapper &other) " << name << ' ' << ++mm[name] << endl; } virtual ~BaseWrapper() { cout << "~BaseWrapper() " << name << ' ' << mm[name]-- << endl; } private: static map<const string, int> mm; // static counter const string name; }; map<const string, int> BaseWrapper::mm; class MySig : BaseWrapper, public boost::signals2::signal<FuncSig> { }; using Sig = MySig; //boost::signals2::signal<FuncSig>; class A { public: A() {} boost::signals2::connection add_listener(const string &str, const Sig::slot_type &slot) { //my_map.emplace(str, Sig{}); auto x = my_map.emplace(std::piecewise_construct, std::forward_as_tuple(str), std::forward_as_tuple()); return x.first->second.connect(slot); } void remove_listener(const string &str) { auto it = my_map.find(str); if (it != my_map.cend()) { my_map.erase(it); } } void call(const string &str) { auto it = my_map.find(str); if (it != my_map.cend()) { it->second(); } } private: map<const string, Sig> my_map; }; class Functor : BaseWrapper { public: Functor(A *a_, int x_) : BaseWrapper{"Functor"}, a{a_}, x{x_} {} void operator()() const { cout << " this == " << this << endl; cout << " x == " << x << endl; a->remove_listener("hello"); cout << " this == " << this << endl; cout << " x == " << x << endl; } private: A *a; int x; }; int main() { A a; { Functor fun{&a, 4}; a.add_listener("hello", fun); } a.call("hello"); std::cout << "end" << endl; return 0; } /////////////////////////////////////////////////////////////////////
Just to show an example (not using Signals2), where we can self-destruct a Functor: This is unsafe (disaster) code. /////////////////////////////////////////////////////////////////// #include <iostream> #include <map> #include <functional> #include <algorithm> using namespace std; using FuncSig = void(); using Func = function<FuncSig>; class Functor { public: Functor(map<const string, Func> *m_, const string &key_) : m{m_}, key{key_} { cout << "Functor() " << key << endl; } Functor(const Functor &other) : m{other.m}, key{other.key} { cout << "Functor(const Functor &other) " << key << endl; } ~Functor() { cout << "~Functor() " << key << endl; } void operator()() const { cout << " this == " << this << endl; cout << " key == " << key << endl; auto it = m->find(key); if (it != m->cend()) { m->erase(it); // self-destruct !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } cout << " still running code from class (if constructor has already run, we're in deep trouble... accessing destructed data...)" << endl;; cout << " this == " << this << endl; cout << " key == " << key << endl; } private: map<const string, Func> *m; const string key; }; int main() { map<const string, Func> m; auto x = m.emplace("hello", Functor(&m, "hello")); x.first->second(); return 0; } ///////////////////////////////////////////////////////////////////
Oh and an absolute basics question... Does the connect function copy functors? I believe it does, so the code below would be safe. (Is that correct????) Thanks boost::signals2::signal<void()> sig; { Slot_Functor slot; sig.connect(slot); } sig(); Is there any case, where a slot could go out of scope before the signal is called?? If functors (and lambdas) are nicely copied, then I believe that the slot will always be in scope when it is called. (??) Thanks
participants (1)
-
nice sw123