From c2114fa0af73b42bc1ef302f7511ef87690cc0b1 Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Fri, 26 Dec 2025 18:10:42 +0100 Subject: TECO_DEFINE_STATE() no longer constructs callback names for mandatory callbacks, but tries to use static assertions * Requiring state callbacks by generating their names (e.g. NAME##_input) has several disadvantages: * The callback is not explicitly referenced when the state is defined. So an unintroduced reader will see some static function, which is nowhere referenced and still doesn't cause "unused" warnings. * You cannot choose the name of function that implements the callback freely. * In "substates" you need to generate a callback function if you want to provide a default. You also need to provide dummy wrapper functions whenever you want to reuse some existing function as the implementation. * Instead, we are now using static assertions to check whether certain callbacks have been implemented. Unfortunately, this does not work on all compilers. In particular GCC won't consider references to state objects fully constant (even though they are) and does not allow them in _Static_assert (G_STATIC_ASSERT). This could only be made to work in newer GCC with -std=c2x or -std=gnu23 in combination with constexpr. It does work on Clang, though. So I introduced TECO_ASSERT_SAFE() which also passes if the expression is *not* constant. These static assertions are not crucial - they do not check anything that can differ between systems. So we can always rely on the checks performed by FreeBSD CI for instance. Also, you will of course quickly notice missing callbacks at runtime - with and without additional runtime assertions. * All mandatory callbacks must still be explicitly initialized in the TECO_DEFINE_STATE calls. * After getting rid of generated callback implementations, the TECO_DEFINE_STATE macros can finally be qualified with `static`. * The TECO_DECLARE_STATE() macro has been removed. It no longer abstracts anything and cannot be used to declare static teco_state_t anyway. Also TECO_DEFINE_UNDO_CALL() also doesn't have a DECLARE counterpart. --- src/search.c | 58 +++++++++++++++++++++++++++------------------------------- 1 file changed, 27 insertions(+), 31 deletions(-) (limited to 'src/search.c') diff --git a/src/search.c b/src/search.c index 83e22ef..2a39b59 100644 --- a/src/search.c +++ b/src/search.c @@ -882,7 +882,6 @@ teco_state_search_done(teco_machine_main_t *ctx, const teco_string_t *str, GErro TECO_DEFINE_STATE_EXPECTSTRING(NAME, \ .initial_cb = (teco_state_initial_cb_t)teco_state_search_initial, \ .expectstring.process_cb = teco_state_search_process, \ - .expectstring.done_cb = NAME##_done, \ ##__VA_ARGS__ \ ) @@ -956,7 +955,9 @@ teco_state_search_done(teco_machine_main_t *ctx, const teco_string_t *str, GErro * Changing the results in the search being reperformed * from the beginning. */ -TECO_DEFINE_STATE_SEARCH(teco_state_search); +TECO_DEFINE_STATE_SEARCH(teco_state_search, + .expectstring.done_cb = teco_state_search_done +); static gboolean teco_state_search_all_initial(teco_machine_main_t *ctx, GError **error) @@ -1074,7 +1075,8 @@ teco_state_search_all_done(teco_machine_main_t *ctx, const teco_string_t *str, G * This is probably not very useful in practice, so it's not documented. */ TECO_DEFINE_STATE_SEARCH(teco_state_search_all, - .initial_cb = (teco_state_initial_cb_t)teco_state_search_all_initial + .initial_cb = (teco_state_initial_cb_t)teco_state_search_all_initial, + .expectstring.done_cb = teco_state_search_all_done ); static teco_state_t * @@ -1157,7 +1159,9 @@ teco_state_search_kill_done(teco_machine_main_t *ctx, const teco_string_t *str, /* * ::FK is possible but doesn't make much sense, so it's undocumented. */ -TECO_DEFINE_STATE_SEARCH(teco_state_search_kill); +TECO_DEFINE_STATE_SEARCH(teco_state_search_kill, + .expectstring.done_cb = teco_state_search_kill_done +); static teco_state_t * teco_state_search_delete_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) @@ -1200,7 +1204,9 @@ teco_state_search_delete_done(teco_machine_main_t *ctx, const teco_string_t *str * Searches for just like the regular search command * (\fBS\fP) but when found deletes the entire occurrence. */ -TECO_DEFINE_STATE_SEARCH(teco_state_search_delete); +TECO_DEFINE_STATE_SEARCH(teco_state_search_delete, + .expectstring.done_cb = teco_state_search_delete_done +); static gboolean teco_state_replace_insert_initial(teco_machine_main_t *ctx, GError **error) @@ -1226,16 +1232,7 @@ teco_state_replace_insert_initial(teco_machine_main_t *ctx, GError **error) return TRUE; } -static teco_state_t * -teco_state_replace_insert_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) -{ - return teco_state_insert_done(ctx, str, error); -} - -/* - * FIXME: Could be static - */ -TECO_DEFINE_STATE_INSERT(teco_state_replace_insert, +static TECO_DEFINE_STATE_INSERT(teco_state_replace_insert, .initial_cb = (teco_state_initial_cb_t)teco_state_replace_insert_initial ); @@ -1245,10 +1242,9 @@ teco_state_replace_ignore_done(teco_machine_main_t *ctx, const teco_string_t *st return &teco_state_start; } -/* - * FIXME: Could be static - */ -TECO_DEFINE_STATE_EXPECTSTRING(teco_state_replace_ignore); +static TECO_DEFINE_STATE_EXPECTSTRING(teco_state_replace_ignore, + .expectstring.done_cb = teco_state_replace_ignore_done +); static teco_state_t * teco_state_replace_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) @@ -1291,7 +1287,8 @@ teco_state_replace_done(teco_machine_main_t *ctx, const teco_string_t *str, GErr * immediately and interactively. */ TECO_DEFINE_STATE_SEARCH(teco_state_replace, - .expectstring.last = FALSE + .expectstring.last = FALSE, + .expectstring.done_cb = teco_state_replace_done ); static teco_state_t * @@ -1321,11 +1318,9 @@ teco_state_replace_default_insert_done(teco_machine_main_t *ctx, const teco_stri return &teco_state_start; } -/* - * FIXME: Could be static - */ -TECO_DEFINE_STATE_INSERT(teco_state_replace_default_insert, - .initial_cb = (teco_state_initial_cb_t)teco_state_replace_insert_initial +static TECO_DEFINE_STATE_INSERT(teco_state_replace_default_insert, + .initial_cb = (teco_state_initial_cb_t)teco_state_replace_insert_initial, + .expectstring.done_cb = teco_state_replace_default_insert_done ); static teco_state_t * @@ -1346,10 +1341,9 @@ teco_state_replace_default_ignore_done(teco_machine_main_t *ctx, const teco_stri return &teco_state_start; } -/* - * FIXME: Could be static - */ -TECO_DEFINE_STATE_EXPECTSTRING(teco_state_replace_default_ignore); +static TECO_DEFINE_STATE_EXPECTSTRING(teco_state_replace_default_ignore, + .expectstring.done_cb = teco_state_replace_default_ignore_done +); static teco_state_t * teco_state_replace_default_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) @@ -1391,7 +1385,8 @@ teco_state_replace_default_done(teco_machine_main_t *ctx, const teco_string_t *s * register is implied instead. */ TECO_DEFINE_STATE_SEARCH(teco_state_replace_default, - .expectstring.last = FALSE + .expectstring.last = FALSE, + .expectstring.done_cb = teco_state_replace_default_done ); static teco_state_t * @@ -1434,6 +1429,7 @@ teco_state_replace_default_all_done(teco_machine_main_t *ctx, const teco_string_ * . */ TECO_DEFINE_STATE_SEARCH(teco_state_replace_default_all, + .initial_cb = (teco_state_initial_cb_t)teco_state_search_all_initial, .expectstring.last = FALSE, - .initial_cb = (teco_state_initial_cb_t)teco_state_search_all_initial + .expectstring.done_cb = teco_state_replace_default_all_done ); -- cgit v1.2.3