diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-05-31 14:42:03 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-06-08 03:52:58 -0600 |
commit | 121994d85648469ea498b52cef83e4609496a7b7 (patch) | |
tree | a06d05b01afd2dab7c3d2734d041acff4db3f362 | |
parent | c3a0b37bf0229c89bd78b5ddf140188410aa3597 (diff) |
libmisc: obj.h: Use LM_DEFAPPEND() to implement magic LO_BOX()
-rw-r--r-- | .editorconfig | 2 | ||||
-rw-r--r-- | GNUmakefile | 4 | ||||
-rw-r--r-- | libmisc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libmisc/include/libmisc/obj.h | 23 | ||||
-rw-r--r-- | libmisc/tests/test_obj_autobox.c | 83 | ||||
-rwxr-xr-x | libmisc/tests/test_obj_autobox.c.gen | 17 |
6 files changed, 129 insertions, 1 deletions
diff --git a/.editorconfig b/.editorconfig index b7ad057..565f8b9 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,libusb/include/libusb/tusb_helpers.h.gen}] diff --git a/GNUmakefile b/GNUmakefile index fa76266..c2cf709 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/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt index 07f154b..ef254f9 100644 --- a/libmisc/CMakeLists.txt +++ b/libmisc/CMakeLists.txt @@ -33,5 +33,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/obj.h b/libmisc/include/libmisc/obj.h index 749a9fc..3467d5b 100644 --- a/libmisc/include/libmisc/obj.h +++ b/libmisc/include/libmisc/obj.h @@ -64,6 +64,8 @@ #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, _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){ \ @@ -75,6 +77,25 @@ /* empty */ /** + * `LO_BOX(iface_name, obj)` boxes `obj` as a `lo_interface + * 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 \ + LM_FOREACH_TUPLE(_LO_REGISTRY_##_ARG_iface_name, \ + _LO_BOX, _ARG_iface_name, obj)) +#define LO_BOX2(_ARG_iface_name, obj) _Generic((obj), \ + 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, _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}`. */ #define LO_NULL(_ARG_iface_name) ((lo_interface _ARG_iface_name){}) @@ -112,6 +133,8 @@ 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), \ 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" |