diff options
-rw-r--r-- | .editorconfig | 2 | ||||
-rw-r--r-- | GNUmakefile | 4 | ||||
-rw-r--r-- | cmd/sbc_harness/main.c | 10 | ||||
-rw-r--r-- | lib9p/tests/test_server/main.c | 10 | ||||
-rw-r--r-- | lib9p_util/include/util9p/static.h | 16 | ||||
-rw-r--r-- | libmisc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libmisc/include/libmisc/fmt.h | 52 | ||||
-rw-r--r-- | libmisc/include/libmisc/obj.h | 61 | ||||
-rw-r--r-- | libmisc/tests/test_obj.c | 2 | ||||
-rw-r--r-- | libmisc/tests/test_obj_autobox.c | 83 | ||||
-rwxr-xr-x | libmisc/tests/test_obj_autobox.c.gen | 17 | ||||
-rw-r--r-- | libmisc/tests/test_obj_nest.c | 11 |
12 files changed, 199 insertions, 70 deletions
diff --git a/.editorconfig b/.editorconfig index 42c5a67..1b42b25 100644 --- a/.editorconfig +++ b/.editorconfig @@ -45,7 +45,7 @@ _mode = gitignore # By specific filename (non-lib9p) ############################################# -[{build-aux/embed-sources.h.gen,build-aux/valgrind}] +[{build-aux/embed-sources.h.gen,build-aux/valgrind,libmisc/tests/test_obj_autobox.c.gen}] _mode = sh [{build-aux/lint-{src,bin},build-aux/gcov-prune,libmisc/error_generated.c.gen,libusb/include/libusb/tusb_helpers.h.gen}] diff --git a/GNUmakefile b/GNUmakefile index 51f01aa..db87633 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -33,6 +33,10 @@ generate/files += 3rd-party/COPYING.newlib.txt 3rd-party/COPYING.newlib.txt: /usr/share/licenses/arm-none-eabi-newlib/COPYING.NEWLIB cp $< $@ +generate/files += libmisc/tests/test_obj_autobox.c +libmisc/tests/test_obj_autobox.c: %: %.gen libmisc/tests/test_obj_nest.c + $^ $@ + generate/files += 3rd-party/linux-errno.txt 3rd-party/linux-errno.txt: lib9p/linux-errno.txt.gen $< $(linux.git) $@ diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c index 82519ae..6f53c0b 100644 --- a/cmd/sbc_harness/main.c +++ b/cmd/sbc_harness/main.c @@ -54,11 +54,11 @@ enum { PATH_BASE = __COUNTER__ }; #define STATIC_FILE(STRNAME, ...) UTIL9P_STATIC_FILE(PATH_COUNTER, STRNAME, __VA_ARGS__) #define STATIC_DIR(STRNAME, ...) UTIL9P_STATIC_DIR(PATH_COUNTER, STRNAME, __VA_ARGS__) -#define API_FILE(STRNAME, SYMNAME, ...) \ - LO_BOX(lib9p_srv_file, &((struct SYMNAME##_file){ \ - .name = STRNAME, \ - .pathnum = PATH_COUNTER \ - __VA_OPT__(,) __VA_ARGS__ \ +#define API_FILE(STRNAME, SYMNAME, ...) \ + lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \ + .name = STRNAME, \ + .pathnum = PATH_COUNTER \ + __VA_OPT__(,) __VA_ARGS__ \ })) static struct lib9p_srv_file root = diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c index 0431c40..e28d19e 100644 --- a/lib9p/tests/test_server/main.c +++ b/lib9p/tests/test_server/main.c @@ -63,11 +63,11 @@ static struct { #define STATIC_DIR(N, STRNAME, ...) \ UTIL9P_STATIC_DIR(N, STRNAME, __VA_ARGS__) -#define API_FILE(N, STRNAME, SYMNAME, ...) \ - LO_BOX(lib9p_srv_file, &((struct SYMNAME##_file){ \ - .name = STRNAME, \ - .pathnum = N \ - __VA_OPT__(,) __VA_ARGS__ \ +#define API_FILE(N, STRNAME, SYMNAME, ...) \ + lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \ + .name = STRNAME, \ + .pathnum = N \ + __VA_OPT__(,) __VA_ARGS__ \ })) static struct lib9p_srv_file root = diff --git a/lib9p_util/include/util9p/static.h b/lib9p_util/include/util9p/static.h index c204607..4bb24c4 100644 --- a/lib9p_util/include/util9p/static.h +++ b/lib9p_util/include/util9p/static.h @@ -51,10 +51,10 @@ struct util9p_static_dir { }; LO_IMPLEMENTATION_H(lib9p_srv_file, struct util9p_static_dir, util9p_static_dir); -#define UTIL9P_STATIC_DIR(PATH, STRNAME, ...) \ - LO_BOX(lib9p_srv_file, &((struct util9p_static_dir){ \ - .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0555), \ - .members = { __VA_ARGS__ LO_NULL(lib9p_srv_file) }, \ +#define UTIL9P_STATIC_DIR(PATH, STRNAME, ...) \ + lo_box_util9p_static_dir_as_lib9p_srv_file(&((struct util9p_static_dir){ \ + .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0555), \ + .members = { __VA_ARGS__ LO_NULL(lib9p_srv_file) }, \ })) /* File ***********************************************************************/ @@ -68,10 +68,10 @@ struct util9p_static_file { }; LO_IMPLEMENTATION_H(lib9p_srv_file, struct util9p_static_file, util9p_static_file); -#define UTIL9P_STATIC_FILE(PATH, STRNAME, ...) \ - LO_BOX(lib9p_srv_file, &((struct util9p_static_file){ \ - .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0444), \ - __VA_ARGS__ \ +#define UTIL9P_STATIC_FILE(PATH, STRNAME, ...) \ + lo_box_util9p_static_file_as_lib9p_srv_file(&((struct util9p_static_file){ \ + .c = UTIL9P_STATIC_COMMON(PATH, STRNAME, 0444), \ + __VA_ARGS__ \ })) #endif /* _UTIL9P_STATIC_H_ */ diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt index 9bb282b..3487f76 100644 --- a/libmisc/CMakeLists.txt +++ b/libmisc/CMakeLists.txt @@ -35,5 +35,6 @@ add_lib_test(libmisc test_macro) add_lib_test(libmisc test_map) add_lib_test(libmisc test_obj) add_lib_test(libmisc test_obj_nest) +add_lib_test(libmisc test_obj_autobox) add_lib_test(libmisc test_private) add_lib_test(libmisc test_rand) diff --git a/libmisc/include/libmisc/fmt.h b/libmisc/include/libmisc/fmt.h index 135e48b..6c04d99 100644 --- a/libmisc/include/libmisc/fmt.h +++ b/libmisc/include/libmisc/fmt.h @@ -113,27 +113,27 @@ struct fmt_buf { }; LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf); -#define fmt_snprint(buf, n, ...) ({ \ - struct fmt_buf _w = { .dat = buf, .cap = n }; \ - lo_interface fmt_dest w = LO_BOX(fmt_dest, &_w); \ - fmt_print(w, __VA_ARGS__); \ - if (_w.len < _w.cap) \ - ((char *)_w.dat)[_w.len] = '\0'; \ - _w.len; \ +#define fmt_snprint(buf, n, ...) ({ \ + struct fmt_buf _w = { .dat = buf, .cap = n }; \ + lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \ + fmt_print(w, __VA_ARGS__); \ + if (_w.len < _w.cap) \ + ((char *)_w.dat)[_w.len] = '\0'; \ + _w.len; \ }) -#define fmt_asprint(...) ({ \ - struct fmt_buf _w = {}; \ - lo_interface fmt_dest w = LO_BOX(fmt_dest, &_w); \ - fmt_print(w, __VA_ARGS__); \ - while (_w.cap <= _w.len) { \ - _w.cap = _w.len + 1; \ - _w.len = 0; \ - _w.dat = realloc(_w.dat, _w.cap); \ - fmt_print(w, __VA_ARGS__); \ - } \ - ((char *)_w.dat)[_w.len] = '\0'; \ - _w.dat; \ +#define fmt_asprint(...) ({ \ + struct fmt_buf _w = {}; \ + lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \ + fmt_print(w, __VA_ARGS__); \ + while (_w.cap <= _w.len) { \ + _w.cap = _w.len + 1; \ + _w.len = 0; \ + _w.dat = realloc(_w.dat, _w.cap); \ + fmt_print(w, __VA_ARGS__); \ + } \ + ((char *)_w.dat)[_w.len] = '\0'; \ + _w.dat; \ }) /* justify ********************************************************************/ @@ -145,13 +145,13 @@ LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf); fmt_print_byte(w, fillchar); \ } while (0) -#define fmt_print_rjust(w, width, fillchar, ...) do { \ - struct fmt_buf _discard = {}; \ - lo_interface fmt_dest discard = LO_BOX2(fmt_dest, &_discard); \ - fmt_print2(discard, __VA_ARGS__); \ - while (_discard.len++ < width) \ - fmt_print_byte(w, fillchar); \ - fmt_print2(w, __VA_ARGS__); \ +#define fmt_print_rjust(w, width, fillchar, ...) do { \ + struct fmt_buf _discard = {}; \ + lo_interface fmt_dest discard = lo_box_fmt_buf_as_fmt_dest(&_discard); \ + fmt_print2(discard, __VA_ARGS__); \ + while (_discard.len++ < width) \ + fmt_print_byte(w, fillchar); \ + fmt_print2(w, __VA_ARGS__); \ } while (0) void fmt_print_base16_u8_(lo_interface fmt_dest w, uint8_t x); diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h index 6afa391..3467d5b 100644 --- a/libmisc/include/libmisc/obj.h +++ b/libmisc/include/libmisc/obj.h @@ -30,6 +30,10 @@ * * Use `lo_interface {iface_name}` as the type of this interface; it * should not be a pointer type. + * + * If there are any LO_NEST interfaces, this will define a + * `lo_box_{iface_name}_as_{wrapped_iface_name}(obj)` function for + * each. */ #define LO_NEST(_ARG_child_iface_name) \ (lo_nest, _ARG_child_iface_name) @@ -52,29 +56,31 @@ #define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) \ const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; \ LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE2) -#define _LO_IFACE_VTABLE_lo_func(_ARG_ret_type, _ARG_func_name, ...) \ +#define _LO_IFACE_VTABLE_lo_func(_ARG_ret_type, _ARG_func_name, ...) \ _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); #define _LO_IFACE_VTABLE_indirect() _LO_IFACE_VTABLE #define _LO_IFACE_VTABLE2(...) _LM_DEFER2(_LO_IFACE_VTABLE_indirect)()(__VA_ARGS__) #define _LO_IFACE_PROTO(_ARG_iface_name, _tuple_typ, ...) _LO_IFACE_PROTO_##_tuple_typ(_ARG_iface_name, __VA_ARGS__) -#define _LO_IFACE_PROTO_lo_nest(_ARG_iface_name, _ARG_child_iface_name) \ - LM_DEFAPPEND(_LO_REGISTRY_##_ARG_child_iface_name, \ - (lo_interface _ARG_iface_name, _LO_BOX_##_ARG_iface_name##_as_##_ARG_child_iface_name)); \ - LM_DEFAPPEND(_LO_BOX_##_ARG_iface_name##_as_##_ARG_child_iface_name(obj), \ - { .self = obj.self, .vtable = obj.vtable->_lo_##_ARG_child_iface_name##_vtable }); +#define _LO_IFACE_PROTO_lo_nest(_ARG_iface_name, _ARG_child_iface_name) \ + LM_DEFAPPEND(_LO_REGISTRY_##_ARG_child_iface_name, \ + (lo_interface _ARG_iface_name, _ARG_iface_name)); \ + LM_ALWAYS_INLINE static lo_interface _ARG_child_iface_name \ + lo_box_##_ARG_iface_name##_as_##_ARG_child_iface_name(lo_interface _ARG_iface_name obj) { \ + return (lo_interface _ARG_child_iface_name){ \ + .self = obj.self, \ + .vtable = obj.vtable->_lo_##_ARG_child_iface_name##_vtable, \ + }; \ + } #define _LO_IFACE_PROTO_lo_func(_ARG_iface_name, _ARG_ret_type, _ARG_func_name, ...) \ /* empty */ /** * `LO_BOX(iface_name, obj)` boxes `obj` as a `lo_interface - * iface_name`. `obj` must be one of: - * - * - A pointer to a value that implements `lo_interface iface_name` - * - An already-boxed instance of `lo_interface iface_name` - * - An already-boxed instance of another interface that - * `iface_name` inherits from. + * iface_name`; if `obj` is not already a `lo_interface iface_name` + * box, then it simply calls the appropriate + * lo_box_{XXX}_as_{iface_name}() function/macro. */ #define LO_BOX(_ARG_iface_name, obj) _Generic((obj), \ lo_interface _ARG_iface_name: obj \ @@ -84,8 +90,10 @@ lo_interface _ARG_iface_name: obj \ LM_FOREACH_TUPLE2(_LO_REGISTRY_##_ARG_iface_name, \ _LO_BOX, _ARG_iface_name, obj)) -#define _LO_BOX(_ARG_iface_name, obj, typ, boxfn) \ - , typ: (lo_interface _ARG_iface_name)boxfn(obj) +#define _LO_BOX(_ARG_iface_name, _ARG_obj, _ARG_typ, _ARG_typnam) \ + , _ARG_typ: lo_box_##_ARG_typnam##_as_##_ARG_iface_name(_LO_coerce(_ARG_typ, _ARG_obj)) +#define _LO_coerce(typ, obj) \ + _Generic((obj), typ: (obj), default: *((typ*)0)) /** * `LO_NULL(iface_name)` is the null/nil/zero value for `lo_interface {iface_name}`. @@ -115,17 +123,24 @@ * file to declare that `{impl_type}` implements the `{iface_name}` * interface with functions named `{impl_name}_{method_name}`. * + * This will also define a `lo_box_{impl_name}_as_{iface_name}(obj)` + * const-expr macro. + * * You must also call the LO_IMPLEMENTATION_C in a single .c file. */ -#define LO_IMPLEMENTATION_H(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \ - /* Vtable. */ \ - extern const struct _lo_##_ARG_iface_name##_vtable \ - _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable; \ - /* Boxing. */ \ - LM_DEFAPPEND(_LO_REGISTRY_##_ARG_iface_name, \ - (_ARG_impl_type *, _LO_BOX_##_ARG_impl_name##_as_##_ARG_iface_name)); \ - LM_DEFAPPEND(_LO_BOX_##_ARG_impl_name##_as_##_ARG_iface_name(obj), \ - { .self = obj, .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable }); \ +#define LO_IMPLEMENTATION_H(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \ + /* Vtable. */ \ + extern const struct _lo_##_ARG_iface_name##_vtable \ + _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable; \ + /* Boxing. */ \ + LM_DEFAPPEND(_LO_REGISTRY_##_ARG_iface_name, \ + (_ARG_impl_type *, _ARG_impl_name)); \ + LM_DEFAPPEND(lo_box_##_ARG_impl_name##_as_##_ARG_iface_name(_ARG_self), ( \ + (lo_interface _ARG_iface_name){ \ + .self = (_ARG_self), \ + .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable, \ + } \ + )); \ LM_FORCE_SEMICOLON /** diff --git a/libmisc/tests/test_obj.c b/libmisc/tests/test_obj.c index a13b8c9..687ad4e 100644 --- a/libmisc/tests/test_obj.c +++ b/libmisc/tests/test_obj.c @@ -53,7 +53,7 @@ int main() { struct myclass obj = { .a = MAGIC1, }; - lo_interface frobber iface = LO_BOX(frobber, &obj); + lo_interface frobber iface = lo_box_myclass_as_frobber(&obj); test_assert(LO_CALL(iface, frob) == MAGIC1); test_assert(LO_CALL(iface, frob1, MAGIC2) == MAGIC2); LO_CALL(iface, frob0); diff --git a/libmisc/tests/test_obj_autobox.c b/libmisc/tests/test_obj_autobox.c new file mode 100644 index 0000000..1bf442d --- /dev/null +++ b/libmisc/tests/test_obj_autobox.c @@ -0,0 +1,83 @@ +/* libmisc/tests/test_obj_autobox.c - Generated by `libmisc/tests/test_obj_nest.c libmisc/tests/test_obj_autobox.c`. DO NOT EDIT! */ +/* libmisc/tests/test_obj_nest.c - Tests for <libmisc/obj.h> nesting + * + * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <string.h> /* for memcpy() */ + +#include <libmisc/obj.h> + +#include "test.h" + +/* interfaces *****************************************************************/ + +#define reader_LO_IFACE \ + LO_FUNC(ssize_t, read, void *, size_t) +LO_INTERFACE(reader); + +#define writer_LO_IFACE \ + LO_FUNC(ssize_t, write, void *, size_t) +LO_INTERFACE(writer); + +#define read_writer_LO_IFACE \ + LO_NEST(reader) \ + LO_NEST(writer) +LO_INTERFACE(read_writer); + +/* implementation header ******************************************************/ + +struct myclass { + size_t len; + char buf[512]; +}; +LO_IMPLEMENTATION_H(reader, struct myclass, myclass); +LO_IMPLEMENTATION_H(writer, struct myclass, myclass); +LO_IMPLEMENTATION_H(read_writer, struct myclass, myclass); + +/* implementation main ********************************************************/ + +LO_IMPLEMENTATION_C(reader, struct myclass, myclass, static); +LO_IMPLEMENTATION_C(writer, struct myclass, myclass, static); +LO_IMPLEMENTATION_C(read_writer, struct myclass, myclass, static); + +static ssize_t myclass_read(struct myclass *self, void *buf, size_t count) { + test_assert(self); + if (count > self->len) + count = self->len; + memcpy(buf, self->buf, count); + return count; +} + +static ssize_t myclass_write(struct myclass *self, void *buf, size_t count) { + test_assert(self); + if (self->len) + return -1; + if (count > sizeof(self->buf)) + count = sizeof(self->buf); + memcpy(self->buf, buf, count); + self->len = count; + return count; +} + +/* main test body *************************************************************/ + +int main() { + struct myclass _obj = {}; + lo_interface read_writer obj = LO_BOX(read_writer, &_obj); + test_assert(LO_CALL(obj, write, "Hello", 6) == 6); + char buf[6] = {}; + test_assert(LO_CALL(obj, read, buf, 3) == 3); + test_assert(memcmp(buf, "Hel\0\0\0", 6) == 0); + + lo_interface reader rd = LO_BOX(reader, &_obj); + rd = LO_BOX(reader, obj); + (void) rd; + + lo_interface writer wr = LO_BOX(writer, &_obj); + wr = LO_BOX(writer, obj); + (void) wr; + + return 0; +} diff --git a/libmisc/tests/test_obj_autobox.c.gen b/libmisc/tests/test_obj_autobox.c.gen new file mode 100755 index 0000000..3cfa3d4 --- /dev/null +++ b/libmisc/tests/test_obj_autobox.c.gen @@ -0,0 +1,17 @@ +#!/bin/sh +# libmisc/tests/test_obj_autobox.c.gen - Generate tests for LO_BOX() +# +# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +infile=$1 +outfile=$2 + +grep --quiet lo_box_ -- "$infile" + +{ + echo "/* ${outfile} - Generated by \`$*\`. DO NOT EDIT! */" + sed -E 's/lo_box_([a-z0-9_]+)_as_([a-z0-9_]+)\(/LO_BOX(\2, /g' <"$infile" +} >"$outfile" + +! grep -H lo_box_ -- "$outfile" diff --git a/libmisc/tests/test_obj_nest.c b/libmisc/tests/test_obj_nest.c index ba5ac37..66c1efd 100644 --- a/libmisc/tests/test_obj_nest.c +++ b/libmisc/tests/test_obj_nest.c @@ -64,10 +64,19 @@ static ssize_t myclass_write(struct myclass *self, void *buf, size_t count) { int main() { struct myclass _obj = {}; - lo_interface read_writer obj = LO_BOX(read_writer, &_obj); + lo_interface read_writer obj = lo_box_myclass_as_read_writer(&_obj); test_assert(LO_CALL(obj, write, "Hello", 6) == 6); char buf[6] = {}; test_assert(LO_CALL(obj, read, buf, 3) == 3); test_assert(memcmp(buf, "Hel\0\0\0", 6) == 0); + + lo_interface reader rd = lo_box_myclass_as_reader(&_obj); + rd = lo_box_read_writer_as_reader(obj); + (void) rd; + + lo_interface writer wr = lo_box_myclass_as_writer(&_obj); + wr = lo_box_read_writer_as_writer(obj); + (void) wr; + return 0; } |