Skip to content

Instantly share code, notes, and snippets.

@interval1066
Forked from Alia5/Signals.h
Last active September 2, 2020 03:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save interval1066/2b4a707da25a1d23669e9cb8b2dd8203 to your computer and use it in GitHub Desktop.
Save interval1066/2b4a707da25a1d23669e9cb8b2dd8203 to your computer and use it in GitHub Desktop.
C++11 Signals/Slots implementation
#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
@interval1066
Copy link
Author

Original code was full of problems. As I'm currently fascinated with the observer pattern and implementing signals and slots in as simple a way as possible I have corrected the code to work when compiled with gcc 7.5.

@interval1066
Copy link
Author

Added the "functional" std header, and fixed some scope issues, but this does compile, aside from reorder complaints from the compiler.

@interval1066
Copy link
Author

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.

@interval1066
Copy link
Author

interval1066 commented Sep 1, 2020

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.

@interval1066
Copy link
Author

interval1066 commented Sep 2, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment