Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
259 lines (225 sloc) 8.11 KB
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Felipe Lavratti
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
*
* The Customized Signal Slot Class Body Generator
*
*
* This code generates functions with the customized signatures for a signal
* slot event emitting system. Besides being a generic template, it keeps type
* safety, there's absolutely no opaque data, commonly used to generalize
* signatures and bypas type checking on build time.
*
* Our approach create names and signatures from configuration macros. Because
* that, you have to define four macros that will be used by this file and then
* `#include` this file to your .c code.
*
* Below an example of explained .c file to generate a signal slot module where
* the handler takes an `int` and a `char *` as argument and the modudle is
* called "signal_slot_test":
*
* First, we setup the prefixes used to form function and types names. It has
* to finish with `_`.
*
* #define signal_prefix signal_test_
* #define slot_prefix slot_test_
*
* Next the parameters of the handler function that is called when a signal is
* emmited. It is necessary three macros:
* `parameters_declaration`: used to create a list of arguments for internal
* functions, __VA_ARGS__ cannot be removed.
* `parameters_list`: used to create a list of arguments passed to internal
* functions, __VA_ARGS__ cannot be removed.
* `handler_parameters_list`: used only to create arguments passed to user
* handler. Here you can remove __VA_ARGS__, if you keep it you will
* have a pointer to the slot as an argument.
*
* ps: add `_` in front of the argument names to avoid conflicts.
*
* #define parameters_declaration(...) __VA_ARGS__, int _a, char * _b
* #define parameters_list(...) __VA_ARGS__, _a, _b
* #define handler_parameters_list(...) __VA_ARGS__, _a, _b
*
* It's done, now we include this file in the `signal_slot_test.c` at the end:
*
* #include "signalslot.template"
*
* Do not export those macros in a header, since they have no namespacing and
* will cause conflict, this is a point of improvment, since it makes impossible
* to generate code for the header as well.
*
* The header has to be written manually, this is the appropriate header used
* to export the generated code in this example:
*
* #include "linked_list.h"
* #include <stdbool.h>
*
* struct slot_test_private;
* typedef struct slot_test_private slot_test_t;
*
* struct signal_test_private;
* typedef struct signal_test_private signal_test_t;
*
* typedef void (*slot_test_handler_t) (slot_test_t * slot, int, char *);
*
* void signal_test_init(signal_test_t *, slot_demo_handler_t handler);
* void signal_test_deinit(signal_test_t *);
* bool signal_test_is_connected(signal_test_t *, slot_test_t *);
* void signal_test_emit(signal_test_t *, int n, char * buf);
*
* void slot_test_init(slot_test_t *);
* void slot_test_deinit(slot_test_t *);
* void slot_test_connect(slot_test_t *, signal_test_t *);
* void slot_test_disconnect(slot_test_t *, signal_test_t *);
*
* struct signal_test_private
* {
* struct slot_test_private * root;
* };
*
* struct slot_test_private
* {
* signal_test_t * connection;
* slot_test_handler_t handler;
* linked_list_t ll;
* };
*
* ps: We avoided calling this file .c to keep it away from being built directly
* by a build system that build everything that has the .c extension.
*/
#include <stddef.h>
#include <stdbool.h>
#include <reacto/reusables/debug.h>
#include <reacto/reusables/log.h>
#define macro_cat(a, ...) a ## __VA_ARGS__
#define function(m, f) macro_cat(m, f)
#define type_name(m) macro_cat(m, t)
#define handler_declaration(m) macro_cat(m, handler_t)
static void function(slot_prefix, free_from_signal)(type_name(slot_prefix) * obj)
{
obj->connection = NULL;
linked_list_init(obj, ll);
}
void function(signal_prefix, init)(type_name(signal_prefix) * obj)
{
debug_ptr(obj);
obj->root = NULL;
}
void function(signal_prefix, deinit)(type_name(signal_prefix) * obj)
{
debug_ptr(obj);
if (obj->root)
linked_list_free(obj->root, ll, function(slot_prefix, free_from_signal));
obj->root = 0;
}
static int function(slot_prefix, call)(parameters_declaration(type_name(slot_prefix) * obj))
{
debug_ptr(obj, -1);
if (!obj->handler)
return 0;
return obj->handler(handler_parameters_list(obj));
}
/*
* Rules of the Handler:
* Handlers will be called in the order they were inserted.
* If a handler returns anything but 0, the call loop will stop and no other
* connected handler will be called.
*/
void function(signal_prefix, emit)(parameters_declaration(type_name(signal_prefix) * obj))
{
type_name(slot_prefix) * slot_to_be_called = NULL;
debug_ptr(obj);
if (!obj->root)
return;
slot_to_be_called = obj->root;
do
{
int r = function(slot_prefix, call)(parameters_list(slot_to_be_called));
if (r != 0) break;
}
while ((slot_to_be_called = linked_list_next(slot_to_be_called, ll)));
}
bool function(signal_prefix, is_connected)(type_name(signal_prefix) *obj, type_name(slot_prefix) *slot)
{
type_name(slot_prefix) * registered_slot = NULL;
debug_ptr(obj, false);
debug_ptr(slot, false);
if (!obj->root)
return false;
registered_slot = obj->root;
do
{
if (slot == registered_slot) return true;
}
while ((registered_slot = linked_list_next(registered_slot, ll)));
return false;
}
static void function(signal_prefix, connect)(type_name(signal_prefix) *obj, type_name(slot_prefix) *slot)
{
if (function(signal_prefix, is_connected)(obj, slot))
return;
if (!obj->root)
obj->root = slot;
else
linked_list_insert_after(linked_list_last(obj->root, ll), slot, ll);
}
static void function(signal_prefix, disconnect)(type_name(signal_prefix) *obj, type_name(slot_prefix) *slot)
{
obj->root = linked_list_remove(slot, ll);
}
void function(slot_prefix, init)(type_name(slot_prefix) * obj, handler_declaration(slot_prefix) handler)
{
debug_ptr(obj);
linked_list_init(obj, ll);
obj->connection = NULL;
obj->handler = handler;
}
void function(slot_prefix, deinit)(type_name(slot_prefix) * obj)
{
debug_ptr(obj);
if (obj->connection)
function(signal_prefix, disconnect)(obj->connection, obj);
obj->connection = NULL;
obj->handler = NULL;
linked_list_init(obj, ll);
}
void function(slot_prefix, connect)(type_name(slot_prefix) *obj, type_name(signal_prefix)* signal)
{
debug_ptr(obj);
debug_ptr(signal);
function(signal_prefix, connect)(signal, obj);
obj->connection = signal;
}
int function(slot_prefix, disconnect)(type_name(slot_prefix) *obj, type_name(signal_prefix)* signal)
{
debug_ptr(obj, -1);
debug_ptr(signal, -1);
if (obj->connection != signal) {
log_error ("Provided signal is not connected to slot, cannot disconnect.");
return -2;
}
function(signal_prefix, disconnect)(signal, obj);
obj->connection = NULL;
return 0;
}