-
-
Save interval1066/2b4a707da25a1d23669e9cb8b2dd8203 to your computer and use it in GitHub Desktop.
C++11 Signals/Slots implementation
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 characters
#include "sig.h" | |
#include "someob.h" | |
#include <iostream> | |
#include <cstdlib> | |
using namespace sigslot; | |
int | |
main(int argc, char** argv) | |
{ | |
Signal<std::string, int, float> signal; | |
Connection<std::string, int, float> conn = signal.connect(&someObject::differentStuff); | |
signal("The answer: ", 42, 3.3); | |
signal.disconnect(conn); | |
return EXIT_SUCCESS; | |
} |
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 characters
# This is a recursive makefile- do not use in production | |
# convert to canonical make chain when time permits | |
# | |
# recursive file search | |
rfilelist=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rfilelist,$d/,$2)) | |
# the name of program | |
TARGET = sigslot | |
CC = g++ | |
# C++ compiler flags | |
LIBS += #-pthread -lconfig | |
EXT = cc | |
# source files | |
SRC = ./src | |
SRCS := $(call rfilelist,$(SRC),*.$(EXT)) | |
INCLUDES = ./include | |
CFLAGS := -Wall -std=c++11 -c | |
CFLAGS += $(addprefix -I,$(INCLUDES)) | |
OBJS = $(SRCS:%.$(EXT)=%.o) | |
DEBUG_HELPERS = $(SRCS:%.$(EXT)=%.debug) | |
OPTIMIZE_HELPERS = $(SRCS:%.$(EXT)=%.optim) | |
OBJDEBOUT = $(@:%.debug=%.o) | |
OBJOPTOUT = $(@:%.optim=%.o) | |
DEBOUT = $(@:%.debug=%.$(EXT)) | |
OPTOUT = $(@:%.optim=%.$(EXT)) | |
# rules for debug build and optimized build | |
%.debug: %.$(EXT) | |
$(CC) $(CFLAGS) -ggdb -D_DEBUG -DDEBUG -o $(OBJDEBOUT) $(DEBOUT) | |
rm -f $(@.debug=%.debug) | |
touch -f $@ | |
%.optim: %.$(EXT) | |
$(CC) $(CFLAGS) -O2 -DNDEBUG -o $(OBJOPTOUT) $(OPTOUT) | |
rm -f $(@.optim=%.optim) | |
touch -f $@ | |
# rules for object files | |
%.o: %.$(EXT) | |
$(CC) $(CFLAGS) $? | |
# default build | |
all: debug | |
# debug build | |
debug: $(DEBUG_HELPERS) | |
test -s $@ || mkdir $@ | |
$(CC) $(OBJS) -o debug/$(TARGET) $(LIBS) | |
rm -f $(DEBUG_HELPERS) | |
# optimized build | |
optim: $(OPTIMIZE_HELPERS) | |
test -s $@ || mkdir $@ | |
$(CC) $(OBJS) -o optim/$(TARGET) $(LIBS) | |
rm -f $(OPTIMIZE_HELPERS) | |
strip optim/$(TARGET) | |
# clean rule | |
clean: | |
rm -f $(OBJS) $(DEBUG_HELPERS) $(OPTIMIZE_HELPERS) |
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 characters
#ifndef SIG_H_ | |
#define SIG_H_ | |
#include <functional> | |
#include <list> | |
#include <memory> | |
namespace sigslot | |
{ | |
#if __cplusplus < 201703L | |
template <class ForwardIterator, class UnaryPredicate> | |
ForwardIterator remove_if(ForwardIterator first, ForwardIterator last, | |
UnaryPredicate pred) | |
{ | |
ForwardIterator result = first; | |
while (first!=last) { | |
if (!pred(*first)) { | |
*result = std::move(*first); | |
++result; | |
} | |
++first; | |
} | |
return result; | |
} | |
#endif | |
template <typename... Args> | |
class ConnectionItem | |
{ | |
typedef std::function<void(Args...)> Slot; | |
bool _connected = false; | |
Slot _slot; | |
public: | |
ConnectionItem(Slot slot) : _slot(slot) | |
{ | |
_connected = true; | |
} | |
~ConnectionItem() | |
{ | |
_connected = false; | |
} | |
void operator()(Args... args) | |
{ | |
if (_connected && _slot) | |
_slot(args...); | |
} | |
bool isConnected() const | |
{ | |
return _connected; | |
} | |
void disconnect() | |
{ | |
_connected = false; | |
} | |
}; | |
template<typename... Args> | |
class Signal; | |
template<typename... Args> | |
class SConnection; | |
template<typename... Args> | |
class Connection | |
{ | |
typedef std::shared_ptr<ConnectionItem<Args...>> Item; | |
Item _item; | |
void disconnect() | |
{ | |
_item->disconnect(); | |
} | |
bool operator==(const Item &item) | |
{ | |
return item == _item; | |
} | |
friend class Signal<Args...>; | |
friend class SConnection<Args...>; | |
public: | |
Connection(Item item) : _item(item) | |
{ | |
} | |
bool isConnected() const | |
{ | |
return _item->connected; | |
} | |
}; | |
template<typename... Args> | |
class SConnection | |
{ | |
typedef sigslot::Connection<Args...> Connection; | |
bool hasConnection = false; | |
Signal<Args...> *_signal; | |
Connection _connection; | |
public: | |
SConnection() : _signal(nullptr) | |
{ | |
} | |
SConnection(Signal<Args...>& signal, Connection connection) : _signal(&signal), _connection(connection) | |
{ | |
hasConnection = true; | |
} | |
~SConnection() | |
{ | |
if(hasConnection && _signal) | |
_signal->disconnect(_connection); | |
} | |
}; | |
template <typename... Args> | |
class Signal | |
{ | |
typedef std::shared_ptr<ConnectionItem<Args...>> Item; | |
std::list<Item> _items; | |
void removeDisconnected() | |
{ | |
#if __cplusplus < 201703L | |
_items.erase(sigslot::remove_if(_items.begin(), _items.end(), [](Item &item) { | |
#else | |
_items.erase(std::remove_if(_items.begin(), _items.end(), [](Item &item) { | |
#endif | |
return !item->isConnected(); | |
}), _items.end()); | |
} | |
public: | |
typedef std::function<void(Args...)> Slot; | |
typedef sigslot::Connection<Args...> Connection; | |
typedef SConnection<Args...> ScopedConnection; | |
Signal() | |
{ | |
} | |
~Signal() | |
{ | |
for(const auto & item : _items) | |
item->disconnect(); | |
_items.clear(); | |
} | |
template <typename SlotF> | |
Connection connect(SlotF&& slot) | |
{ | |
Item item = std::make_shared<ConnectionItem<Args...>>(std::forward<SlotF>(slot)); | |
_items.push_back(item); | |
return Connection(item); | |
} | |
// Variadics are always better than placeholders. | |
template<class O, typename R, typename ... A> | |
Connection connect(O* o, R(O::*f)(A...)) | |
{ | |
Item item = std::make_shared<ConnectionItem<Args...>>(std::forward<Slot>([=](A... args) { return (o->*f)(args...); })); | |
_items.push_back(item); | |
return Connection(item); | |
} | |
void operator()(Args... args) | |
{ | |
for(const auto & item : _items) | |
(*item)(args...); | |
} | |
bool disconnect(Connection &connection) | |
{ | |
for(const auto & item : _items) { | |
if(connection == item) { | |
connection.disconnect(); | |
removeDisconnected(); | |
return true; | |
} | |
} | |
return false; | |
} | |
}; | |
} | |
#endif |
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 characters
#include "someob.h" | |
void | |
someObject::differentStuff(std::string s, int a, float f) | |
{ | |
std::cout << "different Stuff: " << s << " " << a << " " << f << std::endl; | |
} |
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 characters
#ifndef SOMEOB_H_ | |
#define SOMEOB_H_ | |
#include <iostream> | |
class someObject | |
{ | |
public: | |
static void differentStuff(std::string, int, float); | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Boiled down you need to 1) instantiate a signal object: Signal<args...> sig; Then create a connection end point: Connection<vargs...> slot = sig.connect(&Remote::method); Then raise your signal: sig(args...); then disconnect: sig.dissconect(slot), unless you don't mind (possibly) leaking memory. But this isn't much more onerous than defining signals and slots with preprossessor directives in header files. Qt appears to handle disconnecting signals from slots automagically, but I'll need to look into that. Otherwise I seem to have exactly what I need at a lot less the memory cost of libsigc++ or the overhead of a MOC processor. Honestly libsigc++ is not that heavyweight but never the less mine is a LOT less by far.