From e3fd00bb4e2b7b4cabfb416e59fdf83e331a9374 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sun, 8 Jun 2025 00:32:13 -0600 Subject: libmisc: obj.h: Fix boxing from one interface to another --- libmisc/include/libmisc/obj.h | 14 +++++++------- libmisc/tests/test_obj_nest.c | 9 +++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'libmisc') diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h index 48695af..04438f6 100644 --- a/libmisc/include/libmisc/obj.h +++ b/libmisc/include/libmisc/obj.h @@ -60,13 +60,13 @@ #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_ALWAYS_INLINE static lo_interface _ARG_child_iface_name \ - 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_nest(_ARG_iface_name, _ARG_child_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 */ diff --git a/libmisc/tests/test_obj_nest.c b/libmisc/tests/test_obj_nest.c index d5e563e..66c1efd 100644 --- a/libmisc/tests/test_obj_nest.c +++ b/libmisc/tests/test_obj_nest.c @@ -69,5 +69,14 @@ int main() { 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; } -- cgit v1.2.3-2-g168b From d2e229a8961ace8907645f4068a9d9414d5a36ae Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sat, 31 May 2025 14:42:03 -0600 Subject: libmisc: obj.h: Use LM_DEFAPPEND() to have lo_box_*_as_*() be macros --- libmisc/include/libmisc/obj.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'libmisc') diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h index 04438f6..993bb1a 100644 --- a/libmisc/include/libmisc/obj.h +++ b/libmisc/include/libmisc/obj.h @@ -100,7 +100,7 @@ * interface with functions named `{impl_name}_{method_name}`. * * This will also define a `lo_box_{impl_name}_as_{iface_name}(obj)` - * function. + * const-expr macro. * * You must also call the LO_IMPLEMENTATION_C in a single .c file. */ @@ -109,13 +109,12 @@ extern const struct _lo_##_ARG_iface_name##_vtable \ _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable; \ /* Boxing. */ \ - LM_ALWAYS_INLINE static lo_interface _ARG_iface_name \ - lo_box_##_ARG_impl_name##_as_##_ARG_iface_name(_ARG_impl_type *self) { \ - return (lo_interface _ARG_iface_name){ \ - .self = self, \ + 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 /** -- cgit v1.2.3-2-g168b From c3a0b37bf0229c89bd78b5ddf140188410aa3597 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sun, 8 Jun 2025 03:33:42 -0600 Subject: libmisc: obj.h: Fuss with whitespace --- libmisc/include/libmisc/obj.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'libmisc') diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h index 993bb1a..749a9fc 100644 --- a/libmisc/include/libmisc/obj.h +++ b/libmisc/include/libmisc/obj.h @@ -52,19 +52,22 @@ _LO_IFACE_PROTO, _ARG_iface_name) \ LM_FORCE_SEMICOLON -#define _LO_IFACE_VTABLE(_tuple_typ, ...) _LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__) -#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, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); +#define _LO_IFACE_VTABLE(_tuple_typ, ...) _LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__) +#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, ...) \ + _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(_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_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, \ + .self = obj.self, \ .vtable = obj.vtable->_lo_##_ARG_child_iface_name##_vtable, \ }; \ } -- cgit v1.2.3-2-g168b From 121994d85648469ea498b52cef83e4609496a7b7 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sat, 31 May 2025 14:42:03 -0600 Subject: libmisc: obj.h: Use LM_DEFAPPEND() to implement magic LO_BOX() --- libmisc/CMakeLists.txt | 1 + libmisc/include/libmisc/obj.h | 23 ++++++++++ libmisc/tests/test_obj_autobox.c | 83 ++++++++++++++++++++++++++++++++++++ libmisc/tests/test_obj_autobox.c.gen | 17 ++++++++ 4 files changed, 124 insertions(+) create mode 100644 libmisc/tests/test_obj_autobox.c create mode 100755 libmisc/tests/test_obj_autobox.c.gen (limited to 'libmisc') 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){ \ @@ -74,6 +76,25 @@ #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`; 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}`. */ @@ -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 nesting + * + * Copyright (C) 2025 Luke T. Shumaker + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include /* for memcpy() */ + +#include + +#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 +# 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" -- cgit v1.2.3-2-g168b