Teste semf
Kontakt
?>

Signal Slot

General

The signals in semf are a simple and flexible way to use callback functions.

A signal is like a message, that will be emitted by entering a condition. A slot is a ordinary function or member which can be connected with a signal. First the signals and slots don’t know each other. By connecting a signal with a slot, a program logic is created.

Every time a signal is emitted, the connected slot(s) will be called. A signal can be connected to multiple slots, so by entering a condition multiple functions or members can be called. This behavior helps to easily kick off events in different parts of the application. For example a software timer (e.g. with a time base of one millisecond) often triggers events in multiple application parts. As well a slot can be connected to multiple signals to trigger a function from different software modules. For example a emergency off function often has be triggered from multiple sources.

A simple Example

void slot1()
{
	printf("slot1() was called\n");
}
Signal<> signal;

// ...

signal.connect(&slot1);
signal();

Output:

slot1() was called

Multiple Slots Example

While semf does not require dynamic memory, you have to specify how many slots can be connected. The first template parameter of a signal does exactly that:

void slot1()
{
	printf("slot1() was called\n");
}
void slot2()
{
	printf("slot2() was called\n");
}
Signal<2> signal; //two slots can be connected

// ...

signal.connect(&slot1);
signal.connect(&slot2);
signal();

Output:

slot1() was called
slot2() was called

Disconnecting Slots Example

Slots also can easily be disconnected by using the disconnect() or clear() functions. By using disconnect() only a specific slot is disconnected, in difference to that the clear() function removes all registered slots from a signal.

// ...
void slot1()
{
	printf("slot1() was called\n");
}
void slot2()
{
	printf("slot2() was called\n");
}
Signal<2> signal;

// ...

signal.connect(&slot1);
signal.connect(&slot2);
printf("First call:");
signal();

signal.disconnect(&slot2);
printf("\nSecond call:");
signal();

signal.clear();
printf("\nThird call:");
signal();

Output:

First call:
slot1() was called
slot2() was called

Second call:
slot1() was called

Third call:

Connecting Method Example

The standard usecase in semf is programming in classes. In the following example a member of a simple class implementation is connected and disconnected from a signal.

class MyClass
{
public:
	MyClass(int i) : m_i(i) {}
	void method()
	{
		printf("%i\n", m_i);
	}

private:
	int m_i;
};
MyClass a(1);
MyClass b(2);
MyClass c(3);
Signal<3> signal;

// ...

signal.connect(&a, &MyClass::method);
signal.connect(&b, &MyClass::method);
printf("First call:");
signal();

signal.disconnect(&b, &MyClass::method);
signal.connect(&c, &MyClass::method);
printf("\nSecond call:");
signal();

Output:

First call:
1
2

Second call:
1
3

Signals with parameters

In the past examples the slots had no input parameter. It’s also possible to throw signals with parameters. Parallel to normal functions, signals have no limit for the number of parameters.

void slot1(int i)
{
	printf("%i\n", i);
}
void slot2(char* s, int i)
{
	printf("%s: %i\n", s, i);
}
Signal<1, int> signal1;
Signal<1, char*, int> signal2;

// ...

signal1.connect(&slot1);
signal2.connect(&slot2);
signal1(5);
signal2("hallo", sizeof("hallo"));

Output:

5
hallo: 6

Convert C++ member functions to C function pointers

As usual in embedded software we are writing C++ code based on C libraries and frameworks. Writing object-oriented C++ while dealing with callbacks as C-Style functions or C++ static member functions can mess up your software design. Using signals in combination with lambdas can prevent that mess.

The following example uses FreeRTOS. Typically the function xTaskCreate takes a C function pointer as first parameter containing the task routine. By using signals and lambdas we are able to convert our member function to a C function pointer without loosing the context of the executing object.

class Task
{
public:
  void start()
  {
    static semf::Signal<1, void*> localRun;
    localRun.clear();
    localRun.connect(&run, &semf::Signal<1, void*>::emitSignal);
    xTaskCreate([](void* param)
    {
      localRun(param);
    }, "c++ task", 1024, nullptr, 1, nullptr);
  }
  semf::Signal<1, void*> run;
};

zurück zur Dokumentation