HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Implementing my own signal slot mechanism using C++11

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
signalownmechanismusingimplementingslot

Problem

I programmed in C# and I used the Qt framework. Both of them have their own signal slot mechanism which are really powerful. I looked at several implementations in C++ and based on what I learnt from those sources, I started to implement my own version to better understand how these could work under the hood.

My implementation currently has no support for accessing the return values of the slots, and provides no thread safety. I implemented an auto disconnecting mechanism to manage the slots life cycle and disconnect them if those go out of scope. I posted the source code on GitHub. I've used std::function and variadic templates.

You declare a signal this way:

signal test_signal; // test_signal has an int parameter


You can connect slots with the connect function:

test_signal.connect(&slot_function); // void slot_function(int)
// Connecting to a member function, f is the instance of the object
test_signal.connect(&foo::slot_function, f); // void foo::slot_function(int)


You can connect multiple slots to a signal which are stored in a vector. Every connect method returns a connection class which has a disconnect function which disconnects the slot from the signal. The foo object must inherit from auto_disconnect class for life cycle management. This class stores a vector of connections and when it's destructed it disconnect all of the slots. If you want to emit a signal you can use the overloaded operator().

test_signal(1);


I never done anything like this before and I wanted to spot any design errors, make it more efficient, and have a better understanding about this.

Here is the full source code with example usage:

```
//signal.h
#ifndef SIGNAL_H__
#define SIGNAL_H__

#include
#include

namespace szabi
{
// When a slot is overloaded an ugly cast should be used
// static_cast)>(&T::);
// This helper simplifies this a bit
// overload::of(&T::slot);
template
struct overload
{
template
static constexpr auto o

Solution

A bit late to the party, but I spotted a few mistakes in this otherwise interesting exercise and I thought I would highlight them.

Trying to run the given code as-is results in crashes. Here's why.

First and foremost, std::vector iterators get invalidated whenever an element is erased from the vectors, unless the erasure happens at the vector's very end. Solution: replacing std::vector with a container that guarantees iterator validity over erasures is a must. To this end, std::list might be used.

Then, you must take into account the fact that the connection's slot destroyer lambda body executes when the lambda itself is invoked, not when it's constructed, therefore the iterator of the slot to be destroyed can't be taken from the end of the container within the lambda's body, because by the time the lambda is invoked, other slots might have been added or removed from the container. Solution: take the slot's iterator before constructing the connection object and capture it in the lambda.

Fixing that, makes the code work as expected.

I would also implement the templated method connect(S&& slot, T* instance) in terms of the non-templated one, so avoid duplicating code.

Context

StackExchange Code Review Q#111510, answer score: 4

Revisions (0)

No revisions yet.