Skip to content

Commit f0c920c

Browse files
committed
compile time check on signal callback signature
1 parent 2f86169 commit f0c920c

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

src/gui/gtk.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,100 @@ GtkEventController *(dt_gui_connect_motion)(GtkWidget *widget,
569569
ASSERT_FUNC_TYPE(leave, void(*)(GtkEventControllerMotion *, __typeof__(data))), \
570570
dt_gui_connect_motion(GTK_WIDGET(widget), G_CALLBACK(motion), G_CALLBACK(enter), G_CALLBACK(leave), (data)))
571571

572+
// Enable compile-time checking of signal handler signatures
573+
// Uncomment the _Static_assert to stop compilation on mismatch
574+
// Otherwise errors will be printed at runtime, but only when the signal is connected
575+
// (so all dialogs, menus etc must be opened to see all errors)
576+
// No code is generated when the test is passed
577+
#if 1 && !defined(__cplusplus)
578+
#undef G_CALLBACK
579+
static inline GCallback G_CALLBACK(void *f) { return (GCallback)f; } // as a macro it gets expanded before reaching here
580+
#define DISABLINGPREFIXG_CALLBACK
581+
582+
#define SIGNAME(num, signal, name) !strcmp((signal), #name) ? 1 << num :
583+
#define RETURN_HANDLER(num, ret, instance, data, ...) ret(*)(__typeof__(instance), __VA_OPT__(__VA_ARGS__,) __typeof__(data)) : 1 << num,
584+
#define BOOL_HANDLER(num, instance, data, ...) RETURN_HANDLER(num, gboolean, instance, data, __VA_ARGS__)
585+
#define VOID_HANDLER(num, instance, data, ...) RETURN_HANDLER(num, void, instance, data, __VA_ARGS__)
586+
#define EVENT_HANDLER(instance, data, event) BOOL_HANDLER(0, instance, data, GdkEvent##event*) \
587+
BOOL_HANDLER(0, instance, data, const GdkEvent##event*)
588+
#undef _Static_assert
589+
#undef g_signal_connect
590+
#define g_signal_connect(instance, signal, c_handler, data) do { \
591+
const int required_signature = \
592+
SIGNAME(0, signal, event) \
593+
SIGNAME(0, signal, button-press-event) \
594+
SIGNAME(0, signal, button-release-event) \
595+
SIGNAME(0, signal, motion-notify-event) \
596+
SIGNAME(0, signal, scroll-event) \
597+
SIGNAME(0, signal, enter-notify-event) \
598+
SIGNAME(0, signal, leave-notify-event) \
599+
SIGNAME(0, signal, key-press-event) \
600+
SIGNAME(0, signal, focus-out-event) \
601+
SIGNAME(0, signal, focus-in-event) \
602+
SIGNAME(0, signal, delete-event) \
603+
SIGNAME(0 | 1 << 1 | 1 << 2 , signal, changed) \
604+
SIGNAME(1 | 1 << 4, signal, toggled) \
605+
SIGNAME(1, signal, clicked) \
606+
SIGNAME(1, signal, value-changed) \
607+
SIGNAME(1, signal, value-reset) \
608+
SIGNAME(1, signal, quad-pressed) \
609+
SIGNAME(1, signal, show) \
610+
SIGNAME(1, signal, stopped) \
611+
SIGNAME(1, signal, stop-search) \
612+
SIGNAME(1, signal, day_selected) \
613+
SIGNAME(1, signal, day_selected-double-click) \
614+
SIGNAME(1, signal, selection-changed) \
615+
SIGNAME(1, signal, activate) \
616+
SIGNAME(1, signal, deactivate) \
617+
SIGNAME(1, signal, style-updated) \
618+
SIGNAME(1, signal, color-set) \
619+
SIGNAME(3, signal, moved-to-rect) \
620+
SIGNAME(5, signal, query-tooltip) \
621+
SIGNAME(6, signal, draw) \
622+
SIGNAME(8, signal, cancel) \
623+
SIGNAME(8, signal, size-allocate) \
624+
SIGNAME(8, signal, drag-begin) \
625+
SIGNAME(8, signal, drag-end) \
626+
SIGNAME(9, signal, drag-leave) \
627+
SIGNAME(9, signal, switch-page) \
628+
SIGNAME(10, signal, drag-motion) \
629+
SIGNAME(10, signal, drag-drop) \
630+
SIGNAME(30, signal, pressed) \
631+
SIGNAME(30, signal, released) \
632+
SIGNAME(30, signal, motion) \
633+
SIGNAME(30, signal, enter) \
634+
SIGNAME(30, signal, leave) \
635+
1 << 31; \
636+
const int found_signature = _Generic((DISABLINGPREFIX##c_handler), \
637+
EVENT_HANDLER(instance, data, ) \
638+
EVENT_HANDLER(instance, data, Button) \
639+
EVENT_HANDLER(instance, data, Motion) \
640+
EVENT_HANDLER(instance, data, Scroll) \
641+
EVENT_HANDLER(instance, data, Key) \
642+
EVENT_HANDLER(instance, data, Focus) \
643+
EVENT_HANDLER(instance, data, Crossing) \
644+
VOID_HANDLER(1, instance, data) \
645+
VOID_HANDLER(2, instance, data, char*, GtkTreeIter*) \
646+
VOID_HANDLER(3, instance, data, GdkRectangle*, GdkRectangle*, gboolean, gboolean) \
647+
VOID_HANDLER(4, instance, data, char*) \
648+
BOOL_HANDLER(5, instance, data, gint, gint, gboolean, GtkTooltip*) \
649+
BOOL_HANDLER(6, instance, data, cairo_t*) \
650+
VOID_HANDLER(8, instance, data, GdkRectangle*) \
651+
VOID_HANDLER(8, instance, data, GdkEventSequence*) \
652+
VOID_HANDLER(8, instance, data, GdkDragContext*) \
653+
VOID_HANDLER(9, instance, data, GdkDragContext*, guint) \
654+
VOID_HANDLER(9, instance, data, GtkWidget*, guint) \
655+
BOOL_HANDLER(10, instance, data, GdkDragContext*, const gint, const gint, const guint) \
656+
GCallback : 1 << 30, \
657+
default : 1 << 31); \
658+
if(required_signature == 1 << 31) \
659+
dt_print_nts_ext("%s:%d: connecting unknown signal %s\n", __FILE__, __LINE__, signal); \
660+
else if(!(required_signature & found_signature)) \
661+
dt_print_nts_ext("%s:%d: connecting signal %s to %s with incorrect signature\n", __FILE__, __LINE__, signal, #c_handler); \
662+
/*_Static_assert(required_signature & found_signature, "incorrect function connected to " signal );*/ \
663+
g_signal_connect_data ((instance), (signal), (c_handler), (data), NULL, (GConnectFlags) 0); } while(0)
664+
#endif // __cplusplus
665+
572666
// GTK4 gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(controller));
573667
#define dt_modifier_eq(controller, mask)\
574668
dt_modifier_is(dt_key_modifier_state(), mask)

0 commit comments

Comments
 (0)