OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-cogutil / opencog / util / sigslot.h

115 lines · c ref:
#ifndef __COGUTIL_SIGSLOT_H__
#define __COGUTIL_SIGSLOT_H__

#include <functional>
#include <map>
#include <mutex>

// A signal-slot class, to replace boost::signals2, which is painfully
// over-weight, complex and slow. (Try it -- launch gdb, get into the
// signal, and look at the stack. boost::signals2 uses eleven stack
// frames to do its thing. Eleven! Really!) So this class avoids that.
//
// We don't need anything fancy; this is super-minimalist in design.
// It is thread-safe. It works!
//
// Example usage:
//
// void foo(int x, std::vector<int> y) {
//    printf ("ola %d (%d) [%d %d %d]\n", x, y.size(), y[0], y[1], y[2]);
// }
//
// main () {
//     SigSlot<int, std::vector<int>> siggy;
//     siggy.connect(foo);
//     siggy.emit(42, {68,69,70});
// }
//
// The following also works:
//
// class Bar { public:
//     void baz(int x, std::vector<int> y) {
//         printf ("ciao %d\n", x);
//     }};
//
// main () {
//     Bar bell;
//     SigSlot<int, std::vector<int>> siggy;
//     auto glub = std::bind(&Bar::baz, bell,
//              std::placeholders::_1, std::placeholders::_2);
//     siggy.connect(glub);
//     siggy.emit(42, {68,69,70});
// }

template <typename... ARGS>
class SigSlot
{
	public:
		typedef std::function<void(ARGS...)> slot_type;

	private:
		mutable std::mutex _mtx;
		mutable std::map<int, std::function<void(ARGS...)>> _slots;
		mutable int _slot_id;

	public:
		SigSlot() : _slot_id(0) {}

		// Connect using std::function.
		int connect(std::function<void(ARGS...)> const& fn)
		{
			std::lock_guard<std::mutex> lck(_mtx);
			_slot_id++;
			_slots.insert(std::make_pair(_slot_id, fn));
			return _slot_id;
		}

		// Connect member of a given object.
		//
		// class Bar { public:
		//     void baz(int x, std::vector<int> y) {
		//         printf ("ciao %d\n", x);
		//     }
		// };
		//
		// main() {
		//     Bar bell;
		//     SigSlot<int, std::vector<int>> siggy;
		//     siggy.connect(&Bar::baz, bell);
		//     siggy.emit(42, {68,69,70});
		// }
		template <typename Class>
		int connect(void (Class::*fn)(ARGS...), Class* obj)
		{
			return connect([obj, fn](ARGS... args) { (obj->*fn)(args...); });
		}

		void disconnect(int slotid)
		{
			std::lock_guard<std::mutex> lck(_mtx);
			auto it = _slots.find(slotid);
			if (it != _slots.end())
				_slots.erase(it);
		}

		void disconnect_all()
		{
			std::lock_guard<std::mutex> lck(_mtx);
			_slots.clear();
		}

		// Call everything that's connected.
		void emit(ARGS... p)
		{
			// Avoid taking the lock if there's nothing to deliver.
			if (0 == _slots.size()) return;
			std::lock_guard<std::mutex> lck(_mtx);
			for (auto it : _slots) it.second(p...);
		}

		// Do not take a lock. We want this to be fast.
		size_t size() { return _slots.size(); }
};

#endif /* __COGUTIL_SIGSLOT_H__ */