-
-
Save interval1066/2b4a707da25a1d23669e9cb8b2dd8203 to your computer and use it in GitHub Desktop.
#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 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) |
#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 |
#include "someob.h" | |
void | |
someObject::differentStuff(std::string s, int a, float f) | |
{ | |
std::cout << "different Stuff: " << s << " " << a << " " << f << std::endl; | |
} |
#ifndef SOMEOB_H_ | |
#define SOMEOB_H_ | |
#include <iostream> | |
class someObject | |
{ | |
public: | |
static void differentStuff(std::string, int, float); | |
}; | |
#endif |
After an hour of playing around I got what I wanted out of this code. Build with g++ -Wall -std=c++11 main.cc someob.cc -o test.
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.
Signal.h now builds error-free. Although Peter (the original author) wrote it to be usable under stl 11 his driver program can only be build under 17 due to his use of std::remove_if.