diff options
| author | Robin Haberkorn <rhaberkorn@fmsbw.de> | 2025-12-26 18:10:42 +0100 |
|---|---|---|
| committer | Robin Haberkorn <rhaberkorn@fmsbw.de> | 2025-12-26 18:10:42 +0100 |
| commit | c2114fa0af73b42bc1ef302f7511ef87690cc0b1 (patch) | |
| tree | 3a0ad484d1e4c06e20efa8358f4261f16abe7542 /src/qreg-commands.c | |
| parent | d7330f252e6b0a1326eac6b5fc0b219a7b706eb7 (diff) | |
TECO_DEFINE_STATE() no longer constructs callback names for mandatory callbacks, but tries to use static assertionsHEADmaster-fmsbw-cimaster
* 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.
Diffstat (limited to 'src/qreg-commands.c')
| -rw-r--r-- | src/qreg-commands.c | 64 |
1 files changed, 41 insertions, 23 deletions
diff --git a/src/qreg-commands.c b/src/qreg-commands.c index 83a45b1..5efb140 100644 --- a/src/qreg-commands.c +++ b/src/qreg-commands.c @@ -91,7 +91,9 @@ teco_state_pushqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, * Save Q-Register <q> contents on the global Q-Register push-down * stack. */ -TECO_DEFINE_STATE_EXPECTQREG(teco_state_pushqreg); +TECO_DEFINE_STATE_EXPECTQREG(teco_state_pushqreg, + .expectqreg.got_register_cb = teco_state_pushqreg_got_register +); static teco_state_t * teco_state_popqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, @@ -128,7 +130,8 @@ teco_state_popqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, * Memory is reclaimed on command-line termination. */ TECO_DEFINE_STATE_EXPECTQREG(teco_state_popqreg, - .expectqreg.type = TECO_QREG_OPTIONAL_INIT + .expectqreg.type = TECO_QREG_OPTIONAL_INIT, + .expectqreg.got_register_cb = teco_state_popqreg_got_register ); static teco_state_t * @@ -142,7 +145,8 @@ teco_state_eqcommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, } TECO_DEFINE_STATE_EXPECTQREG(teco_state_eqcommand, - .expectqreg.type = TECO_QREG_OPTIONAL_INIT + .expectqreg.type = TECO_QREG_OPTIONAL_INIT, + .expectqreg.got_register_cb = teco_state_eqcommand_got_register ); static teco_state_t * @@ -187,7 +191,9 @@ teco_state_loadqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr * Undefined Q-Registers will be defined. * The command fails if <file> could not be read. */ -TECO_DEFINE_STATE_EXPECTFILE(teco_state_loadqreg); +TECO_DEFINE_STATE_EXPECTFILE(teco_state_loadqreg, + .expectstring.done_cb = teco_state_loadqreg_done +); static teco_state_t * teco_state_epctcommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, @@ -199,7 +205,9 @@ teco_state_epctcommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, return &teco_state_saveqreg; } -TECO_DEFINE_STATE_EXPECTQREG(teco_state_epctcommand); +TECO_DEFINE_STATE_EXPECTQREG(teco_state_epctcommand, + .expectqreg.got_register_cb = teco_state_epctcommand_got_register +); static teco_state_t * teco_state_saveqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) @@ -231,7 +239,9 @@ teco_state_saveqreg_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr * File names may also be tab-completed and string building * characters are enabled by default. */ -TECO_DEFINE_STATE_EXPECTFILE(teco_state_saveqreg); +TECO_DEFINE_STATE_EXPECTFILE(teco_state_saveqreg, + .expectstring.done_cb = teco_state_saveqreg_done +); static gboolean teco_state_queryqreg_initial(teco_machine_main_t *ctx, GError **error) @@ -346,7 +356,8 @@ teco_state_queryqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, * boolean. */ TECO_DEFINE_STATE_EXPECTQREG(teco_state_queryqreg, - .initial_cb = (teco_state_initial_cb_t)teco_state_queryqreg_initial + .initial_cb = (teco_state_initial_cb_t)teco_state_queryqreg_initial, + .expectqreg.got_register_cb = teco_state_queryqreg_got_register ); static teco_state_t * @@ -360,7 +371,8 @@ teco_state_ctlucommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, } TECO_DEFINE_STATE_EXPECTQREG(teco_state_ctlucommand, - .expectqreg.type = TECO_QREG_OPTIONAL_INIT + .expectqreg.type = TECO_QREG_OPTIONAL_INIT, + .expectqreg.got_register_cb = teco_state_ctlucommand_got_register ); static teco_state_t * @@ -474,7 +486,8 @@ teco_state_setqregstring_nobuilding_done(teco_machine_main_t *ctx, * is desired. */ TECO_DEFINE_STATE_EXPECTSTRING(teco_state_setqregstring_nobuilding, - .expectstring.string_building = FALSE + .expectstring.string_building = FALSE, + .expectstring.done_cb = teco_state_setqregstring_nobuilding_done ); static teco_state_t * @@ -488,7 +501,8 @@ teco_state_eucommand_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, } TECO_DEFINE_STATE_EXPECTQREG(teco_state_eucommand, - .expectqreg.type = TECO_QREG_OPTIONAL_INIT + .expectqreg.type = TECO_QREG_OPTIONAL_INIT, + .expectqreg.got_register_cb = teco_state_eucommand_got_register ); static gboolean @@ -511,12 +525,6 @@ teco_state_setqregstring_building_initial(teco_machine_main_t *ctx, GError **err return TRUE; } -static teco_state_t * -teco_state_setqregstring_building_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) -{ - return teco_state_setqregstring_nobuilding_done(ctx, str, error); -} - /*$ "EU" "EUq" ":EUq" * [c1,c2,...]EUq[string]$ -- Set or append to Q-Register string with string building characters * [c1,c2,...]:EUq[string]$ @@ -529,7 +537,8 @@ teco_state_setqregstring_building_done(teco_machine_main_t *ctx, const teco_stri */ TECO_DEFINE_STATE_EXPECTSTRING(teco_state_setqregstring_building, .initial_cb = (teco_state_initial_cb_t)teco_state_setqregstring_building_initial, - .expectstring.string_building = TRUE + .expectstring.string_building = TRUE, + .expectstring.done_cb = teco_state_setqregstring_nobuilding_done ); static teco_state_t * @@ -582,7 +591,9 @@ teco_state_getqregstring_got_register(teco_machine_main_t *ctx, teco_qreg_t *qre * * Specifying an undefined <q> yields an error. */ -TECO_DEFINE_STATE_EXPECTQREG(teco_state_getqregstring); +TECO_DEFINE_STATE_EXPECTQREG(teco_state_getqregstring, + .expectqreg.got_register_cb = teco_state_getqregstring_got_register +); static teco_state_t * teco_state_setqreginteger_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, @@ -631,7 +642,8 @@ teco_state_setqreginteger_got_register(teco_machine_main_t *ctx, teco_qreg_t *qr * The register is defined if it does not exist. */ TECO_DEFINE_STATE_EXPECTQREG(teco_state_setqreginteger, - .expectqreg.type = TECO_QREG_OPTIONAL_INIT + .expectqreg.type = TECO_QREG_OPTIONAL_INIT, + .expectqreg.got_register_cb = teco_state_setqreginteger_got_register ); static teco_state_t * @@ -665,7 +677,8 @@ teco_state_increaseqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg * <q> will be defined if it does not exist. */ TECO_DEFINE_STATE_EXPECTQREG(teco_state_increaseqreg, - .expectqreg.type = TECO_QREG_OPTIONAL_INIT + .expectqreg.type = TECO_QREG_OPTIONAL_INIT, + .expectqreg.got_register_cb = teco_state_increaseqreg_got_register ); static teco_state_t * @@ -724,7 +737,9 @@ teco_state_macro_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, * (as reported by \fBEE\fP), its contents must be and are checked to be in * valid UTF-8. */ -TECO_DEFINE_STATE_EXPECTQREG(teco_state_macro); +TECO_DEFINE_STATE_EXPECTQREG(teco_state_macro, + .expectqreg.got_register_cb = teco_state_macro_got_register +); static teco_state_t * teco_state_indirect_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) @@ -762,7 +777,9 @@ teco_state_indirect_done(teco_machine_main_t *ctx, const teco_string_t *str, GEr * As all \*(ST code, the contents of <file> must be in valid UTF-8 * even if operating in the \(lqdefault ANSI\(rq mode as configured by \fBED\fP. */ -TECO_DEFINE_STATE_EXPECTFILE(teco_state_indirect); +TECO_DEFINE_STATE_EXPECTFILE(teco_state_indirect, + .expectstring.done_cb = teco_state_indirect_done +); static teco_state_t * teco_state_copytoqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, @@ -865,5 +882,6 @@ teco_state_copytoqreg_got_register(teco_machine_main_t *ctx, teco_qreg_t *qreg, * Register <q> will be created if it is undefined. */ TECO_DEFINE_STATE_EXPECTQREG(teco_state_copytoqreg, - .expectqreg.type = TECO_QREG_OPTIONAL_INIT + .expectqreg.type = TECO_QREG_OPTIONAL_INIT, + .expectqreg.got_register_cb = teco_state_copytoqreg_got_register ); |
