From a83c95e9f46ef695a55fc7a6911e11846da9903c Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Wed, 23 Apr 2025 08:01:36 -0600 Subject: Merge libobj into libmisc --- CMakeLists.txt | 1 - HACKING.md | 5 +- build-aux/measurestack/app_main.py | 9 +- build-aux/measurestack/app_plugins.py | 70 +++------ lib9p/core_gen/c.py | 4 +- lib9p/core_generated.c | 2 +- lib9p/srv_include/lib9p/srv.h | 2 +- libfmt/CMakeLists.txt | 2 - libfmt/include/libfmt/fmt.h | 2 +- libfmt/libmisc.c | 10 ++ libfmt/libobj.c | 17 --- libhw_cr/host_net.c | 2 +- libhw_cr/w5500.c | 2 +- libhw_generic/CMakeLists.txt | 1 - libhw_generic/include/libhw/generic/alarmclock.h | 2 +- libhw_generic/include/libhw/generic/io.h | 2 +- libmisc/CMakeLists.txt | 2 + libmisc/include/libmisc/obj.h | 173 +++++++++++++++++++++++ libmisc/tests/test_obj.c | 61 ++++++++ libmisc/tests/test_obj_nest.c | 73 ++++++++++ libobj/CMakeLists.txt | 13 -- libobj/include/libobj/obj.h | 173 ----------------------- libobj/tests/test.h | 1 - libobj/tests/test_nest.c | 73 ---------- libobj/tests/test_obj.c | 61 -------- 25 files changed, 357 insertions(+), 406 deletions(-) delete mode 100644 libfmt/libobj.c create mode 100644 libmisc/include/libmisc/obj.h create mode 100644 libmisc/tests/test_obj.c create mode 100644 libmisc/tests/test_obj_nest.c delete mode 100644 libobj/CMakeLists.txt delete mode 100644 libobj/include/libobj/obj.h delete mode 120000 libobj/tests/test.h delete mode 100644 libobj/tests/test_nest.c delete mode 100644 libobj/tests/test_obj.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c5251d..659f530 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,7 +124,6 @@ function(add_lib_test arg_libname arg_testname) endfunction() add_subdirectory(libmisc) -add_subdirectory(libobj) add_subdirectory(libfmt) add_subdirectory(libcr) add_subdirectory(libcr_ipc) diff --git a/HACKING.md b/HACKING.md index 574f729..3403b45 100644 --- a/HACKING.md +++ b/HACKING.md @@ -9,9 +9,8 @@ Our own "flavor" of C: GNU C, plus making use of: - - `libobj/`: For Go-like object-oriented programming - - `libmisc/`: Low-level C programming utilities; sort of an augmented - "libc" + - `libmisc/`: Low-level C programming utilities (including Go-like + object-oriented programming); sort of an augmented "libc" Our own tiny cooperative-multitasking OS: diff --git a/build-aux/measurestack/app_main.py b/build-aux/measurestack/app_main.py index 7573146..c670325 100644 --- a/build-aux/measurestack/app_main.py +++ b/build-aux/measurestack/app_main.py @@ -27,8 +27,8 @@ def main( # sbc-harness #################################################### - libobj_plugin = app_plugins.LibObjPlugin(arg_c_fnames) - lib9p_plugin = app_plugins.Lib9PPlugin(arg_base_dir, arg_c_fnames, libobj_plugin) + libmisc_plugin = app_plugins.LibMiscPlugin(arg_c_fnames) + lib9p_plugin = app_plugins.Lib9PPlugin(arg_base_dir, arg_c_fnames, libmisc_plugin) def sbc_is_thread(name: QName) -> int: if str(name).endswith("_cr") and name.base() != BaseName("lib9p_srv_read_cr"): @@ -47,13 +47,12 @@ def main( plugins += [ app_plugins.CmdPlugin(), - libobj_plugin, + libmisc_plugin, app_plugins.PicoFmtPlugin(arg_pico_platform), - app_plugins.LibHWPlugin(arg_pico_platform, libobj_plugin), + app_plugins.LibHWPlugin(arg_pico_platform, libmisc_plugin), app_plugins.LibCRPlugin(), app_plugins.LibCRIPCPlugin(), lib9p_plugin, - app_plugins.LibMiscPlugin(), ] # pico-sdk ####################################################### diff --git a/build-aux/measurestack/app_plugins.py b/build-aux/measurestack/app_plugins.py index bbb0eae..ae2dba9 100644 --- a/build-aux/measurestack/app_plugins.py +++ b/build-aux/measurestack/app_plugins.py @@ -14,7 +14,6 @@ from .util import synthetic_node # pylint: disable=unused-variable __all__ = [ "CmdPlugin", - "LibObjPlugin", "LibHWPlugin", "LibCRPlugin", "LibCRIPCPlugin", @@ -66,7 +65,7 @@ re_lo_implementation = re.compile( re_call_objcall = re.compile(r"LO_CALL\((?P[^,]+), (?P[^,)]+)[,)].*") -class LibObjPlugin: +class LibMiscPlugin: objcalls: dict[str, set[QName]] # method_name => {method_impls} def __init__(self, arg_c_fnames: typing.Collection[str]) -> None: @@ -120,7 +119,6 @@ class LibObjPlugin: def indirect_callees( self, loc: str, line: str ) -> tuple[typing.Collection[QName], bool] | None: - if "/3rd-party/" in loc: return None if m := re_call_objcall.fullmatch(line): @@ -132,16 +130,29 @@ class LibObjPlugin: return None def skipmodels(self) -> dict[BaseName, analyze.SkipModel]: - return {} + return { + BaseName("__assert_msg_fail"): analyze.SkipModel( + {BaseName("__assert_msg_fail")}, self._skipmodel___assert_msg_fail + ), + } + + def _skipmodel___assert_msg_fail( + self, chain: typing.Sequence[QName], call: QName + ) -> bool: + if call.base() in [BaseName("__lm_printf"), BaseName("__lm_light_printf")]: + return any( + c.base() == BaseName("__assert_msg_fail") for c in reversed(chain[:-1]) + ) + return False class LibHWPlugin: pico_platform: str - libobj: LibObjPlugin + libmisc: LibMiscPlugin - def __init__(self, arg_pico_platform: str, libobj: LibObjPlugin) -> None: + def __init__(self, arg_pico_platform: str, libmisc: LibMiscPlugin) -> None: self.pico_platform = arg_pico_platform - self.libobj = libobj + self.libmisc = libmisc def is_intrhandler(self, name: QName) -> bool: return name.base() in [ @@ -175,11 +186,11 @@ class LibHWPlugin: "io_readwritev", ]: if f"{fn}(" in line: - return self.libobj.indirect_callees(loc, f"LO_CALL(x, {fn[3:]})") + return self.libmisc.indirect_callees(loc, f"LO_CALL(x, {fn[3:]})") if "io_read(" in line: - return self.libobj.indirect_callees(loc, "LO_CALL(x, readv)") + return self.libmisc.indirect_callees(loc, "LO_CALL(x, readv)") if "io_writev(" in line: - return self.libobj.indirect_callees(loc, "LO_CALL(x, writev)") + return self.libmisc.indirect_callees(loc, "LO_CALL(x, writev)") if "trigger->cb(trigger->cb_arg)" in line: ret = [ QName("alarmclock_sleep_intrhandler"), @@ -280,11 +291,11 @@ class Lib9PPlugin: self, arg_base_dir: str, arg_c_fnames: typing.Collection[str], - libobj_plugin: LibObjPlugin, + libmisc_plugin: LibMiscPlugin, ) -> None: self.formatters = { x.base() - for x in libobj_plugin.objcalls["format"] + for x in libmisc_plugin.objcalls["format"] if str(x.base()).startswith("lib9p_") } @@ -426,41 +437,6 @@ class Lib9PPlugin: return False -class LibMiscPlugin: - def is_intrhandler(self, name: QName) -> bool: - return False - - def init_array(self) -> typing.Collection[QName]: - return [] - - def extra_includes(self) -> typing.Collection[BaseName]: - return [] - - def extra_nodes(self) -> typing.Collection[Node]: - return [] - - def indirect_callees( - self, loc: str, line: str - ) -> tuple[typing.Collection[QName], bool] | None: - return None - - def skipmodels(self) -> dict[BaseName, analyze.SkipModel]: - return { - BaseName("__assert_msg_fail"): analyze.SkipModel( - {BaseName("__assert_msg_fail")}, self._skipmodel___assert_msg_fail - ), - } - - def _skipmodel___assert_msg_fail( - self, chain: typing.Sequence[QName], call: QName - ) -> bool: - if call.base() in [BaseName("__lm_printf"), BaseName("__lm_light_printf")]: - return any( - c.base() == BaseName("__assert_msg_fail") for c in reversed(chain[:-1]) - ) - return False - - class PicoFmtPlugin: known_fct: dict[BaseName, BaseName] diff --git a/lib9p/core_gen/c.py b/lib9p/core_gen/c.py index c05e457..393766b 100644 --- a/lib9p/core_gen/c.py +++ b/lib9p/core_gen/c.py @@ -35,9 +35,9 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: #include "core_tables.h" #include "core_utf8.h" """ - # libobj vtables ########################################################### + # libmisc/obj.h vtables #################################################### ret += """ -/* libobj vtables *************************************************************/ +/* libmisc/obj.h vtables ******************************************************/ """ for typ in typs: ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) diff --git a/lib9p/core_generated.c b/lib9p/core_generated.c index 3c2ee28..2532d12 100644 --- a/lib9p/core_generated.c +++ b/lib9p/core_generated.c @@ -12,7 +12,7 @@ #include "core_tables.h" #include "core_utf8.h" -/* libobj vtables *************************************************************/ +/* libmisc/obj.h vtables ******************************************************/ #if CONFIG_9P_ENABLE_9P2000 || CONFIG_9P_ENABLE_9P2000_L || CONFIG_9P_ENABLE_9P2000_e || CONFIG_9P_ENABLE_9P2000_p9p || CONFIG_9P_ENABLE_9P2000_u || CONFIG_9P_ENABLE_uninitialized LO_IMPLEMENTATION_C(fmt_formatter, lib9p_tag_t, lib9p_tag, static); LO_IMPLEMENTATION_C(fmt_formatter, lib9p_fid_t, lib9p_fid, static); diff --git a/lib9p/srv_include/lib9p/srv.h b/lib9p/srv_include/lib9p/srv.h index ca80e7b..c40c85a 100644 --- a/lib9p/srv_include/lib9p/srv.h +++ b/lib9p/srv_include/lib9p/srv.h @@ -12,8 +12,8 @@ #include #include #include +#include #include -#include #include diff --git a/libfmt/CMakeLists.txt b/libfmt/CMakeLists.txt index f65d462..dc626f7 100644 --- a/libfmt/CMakeLists.txt +++ b/libfmt/CMakeLists.txt @@ -7,11 +7,9 @@ add_library(libfmt INTERFACE) target_include_directories(libfmt PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_sources(libfmt INTERFACE libmisc.c - libobj.c quote.c ) target_link_libraries(libfmt INTERFACE pico_fmt libmisc - libobj ) diff --git a/libfmt/include/libfmt/fmt.h b/libfmt/include/libfmt/fmt.h index 1b5bde1..692abf2 100644 --- a/libfmt/include/libfmt/fmt.h +++ b/libfmt/include/libfmt/fmt.h @@ -10,7 +10,7 @@ #include #include -#include +#include /** * An object that implements fmt_formatter can be printed using diff --git a/libfmt/libmisc.c b/libfmt/libmisc.c index 803b281..134b9f0 100644 --- a/libfmt/libmisc.c +++ b/libfmt/libmisc.c @@ -61,3 +61,13 @@ int __lm_light_printf(const char *format, ...) { va_end(va); return ret; } + +static void libfmt_conv_formatter(struct fmt_state *state) { + lo_interface fmt_formatter obj = va_arg(*state->args, lo_interface fmt_formatter); + LO_CALL(obj, format, state); +} + +[[gnu::constructor]] +static void libfmt_install_formatter(void) { + fmt_install('v', libfmt_conv_formatter); +} diff --git a/libfmt/libobj.c b/libfmt/libobj.c deleted file mode 100644 index e4b833b..0000000 --- a/libfmt/libobj.c +++ /dev/null @@ -1,17 +0,0 @@ -/* libfmt/libobj.c - Integrate pico-fmt with libobj - * - * Copyright (C) 2025 Luke T. Shumaker - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include - -static void libfmt_conv_formatter(struct fmt_state *state) { - lo_interface fmt_formatter obj = va_arg(*state->args, lo_interface fmt_formatter); - LO_CALL(obj, format, state); -} - -[[gnu::constructor]] -static void libfmt_install_formatter(void) { - fmt_install('v', libfmt_conv_formatter); -} diff --git a/libhw_cr/host_net.c b/libhw_cr/host_net.c index 03cf7db..01f4370 100644 --- a/libhw_cr/host_net.c +++ b/libhw_cr/host_net.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c index 2d6ac48..e676364 100644 --- a/libhw_cr/w5500.c +++ b/libhw_cr/w5500.c @@ -124,7 +124,7 @@ static const char *w5500_state_str(uint8_t state) { } } -/* libobj *********************************************************************/ +/* libmisc/obj.h **************************************************************/ LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcplist, static); LO_IMPLEMENTATION_C(net_stream_listener, struct _w5500_socket, w5500_tcplist, static); diff --git a/libhw_generic/CMakeLists.txt b/libhw_generic/CMakeLists.txt index 603f30b..602168b 100644 --- a/libhw_generic/CMakeLists.txt +++ b/libhw_generic/CMakeLists.txt @@ -7,7 +7,6 @@ add_library(libhw_generic INTERFACE) target_include_directories(libhw_generic PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(libhw_generic INTERFACE libmisc - libobj ) target_sources(libhw_generic INTERFACE diff --git a/libhw_generic/include/libhw/generic/alarmclock.h b/libhw_generic/include/libhw/generic/alarmclock.h index 39050c9..6ce2513 100644 --- a/libhw_generic/include/libhw/generic/alarmclock.h +++ b/libhw_generic/include/libhw/generic/alarmclock.h @@ -9,8 +9,8 @@ #include /* for uint{n}_t and UINT{n}_C */ +#include #include -#include /* Useful constants ***********************************************************/ diff --git a/libhw_generic/include/libhw/generic/io.h b/libhw_generic/include/libhw/generic/io.h index 62ddbb3..6adce8c 100644 --- a/libhw_generic/include/libhw/generic/io.h +++ b/libhw_generic/include/libhw/generic/io.h @@ -11,7 +11,7 @@ #include /* for uintptr_t */ #include /* for ssize_t */ -#include +#include /* structs ********************************************************************/ diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt index 1bce759..8c9fa57 100644 --- a/libmisc/CMakeLists.txt +++ b/libmisc/CMakeLists.txt @@ -20,5 +20,7 @@ add_lib_test(libmisc test_hash) add_lib_test(libmisc test_log) 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_private) add_lib_test(libmisc test_rand) diff --git a/libmisc/include/libmisc/obj.h b/libmisc/include/libmisc/obj.h new file mode 100644 index 0000000..76dde91 --- /dev/null +++ b/libmisc/include/libmisc/obj.h @@ -0,0 +1,173 @@ +/* libmisc/obj.h - A simple Go-ish object system + * + * Copyright (C) 2024-2025 Luke T. Shumaker + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_OBJ_H_ +#define _LIBMISC_OBJ_H_ + +#include + +/** + * Use `lo_interface` similarly to how you would use + * `struct`/`enum`/`union` when writing the type of an interface + * value. + */ +#define lo_interface struct + +/** + * Use `LO_INTERFACE` in a .h file to define an interface. + * + * First define a macro named `{iface_name}_LO_IFACE` consisting of a + * series of calls to LO_NEST and/or LO_FUNC, then call + * `LO_INTERFACE({iface_name})`: + * + * #define myiface_LO_IFACE \ + * LO_NEST(wrapped_iface_name) \ + * LO_FUNC(ret_type, func_name, args...) + * LO_INTERFACE(myiface) + * + * 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) +#define LO_FUNC(_ARG_ret_type, _ARG_func_name, ...) \ + (lo_func, _ARG_ret_type, _ARG_func_name __VA_OPT__(,) __VA_ARGS__) +#define LO_INTERFACE(_ARG_iface_name) \ + struct _lo_##_ARG_iface_name##_vtable { \ + LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ + _LO_IFACE_VTABLE) \ + }; \ + struct _ARG_iface_name { \ + void *self; \ + const struct _lo_##_ARG_iface_name##_vtable *vtable; \ + }; \ + LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ + _LO_IFACE_PROTO, _ARG_iface_name) \ + extern int LM_CAT2_(_HIDDEN_BOGUS_, __COUNTER__) + +#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_VTABLE2(_tuple_typ, ...) _LO_IFACE_VTABLE2_##_tuple_typ(__VA_ARGS__) +#define _LO_IFACE_VTABLE2_lo_nest(_ARG_child_iface_name) const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; LM_FOREACH_TUPLE3(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE3) +#define _LO_IFACE_VTABLE2_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); + +#define _LO_IFACE_VTABLE3(_tuple_typ, ...) _LO_IFACE_VTABLE3_##_tuple_typ(__VA_ARGS__) +#define _LO_IFACE_VTABLE3_lo_nest(_ARG_child_iface_name) const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; LM_FOREACH_TUPLE4(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE4) +#define _LO_IFACE_VTABLE3_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); + +#define _LO_IFACE_VTABLE4(_tuple_typ, ...) _LO_IFACE_VTABLE4_##_tuple_typ(__VA_ARGS__) +#define _LO_IFACE_VTABLE4_lo_nest(_ARG_child_iface_name) static_assert(0, "BUG: libmisc/obj.h cannot nest interfaces more than 4 deep"); +#define _LO_IFACE_VTABLE4_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __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_func(_ARG_iface_name, _ARG_ret_type, _ARG_func_name, ...) \ + /* empty */ + +/** + * `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){0}) + +/** + * `LO_IS_NULL(iface_val)` returns whether `iface_val` is LO_NULL. + */ +#define LO_IS_NULL(_ARG_iface_val) ((_ARG_iface_val).vtable == NULL) + +/** + * `LO_IFACE_EQ(a, b)` returns whether the interface values `a` and + * `b` are the same object. + */ +#define LO_EQ(_ARG_iface_val_a, _ARG_iface_val_b) \ + ((_ARG_iface_val_a).self == (_ARG_iface_val_b).self) + +/** + * Use LO_CALL(obj, method_name, args...) to call a method on an `lo_interface`. + */ +#define LO_CALL(_ARG_obj, _ARG_meth, ...) \ + (_ARG_obj).vtable->_ARG_meth((_ARG_obj).self __VA_OPT__(,) __VA_ARGS__) + +/** + * Use `LO_IMPLEMENTATION_H(iface_name, impl_type, impl_name)` in a .h + * 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)` + * function. + * + * 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_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, \ + .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable, \ + }; \ + } \ + LM_FORCE_SEMICOLON + +/** + * Use `LO_IMPLEMENTATION_C(iface_name, impl_type, impl_name[, static])` in a .c + * file to declare that `{impl_type}` implements the `{iface_name}` interface + * with functions named `{impl_name}_{method_name}`. + * + * You must also call the LO_IMPLEMENTATION_H in the corresponding .h file. + * + * If `iface_name` contains a nested interface, then the + * implementation of the nested interfaces must be declared with + * `LO_IMPLEMENTATION_C` first. + */ +#define LO_IMPLEMENTATION_C(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name, ...) \ + /* Method prototypes. */ \ + LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ + _LO_IMPL_PROTO, _ARG_impl_type, _ARG_impl_name, __VA_ARGS__) \ + /* Vtable. */ \ + const struct _lo_##_ARG_iface_name##_vtable \ + _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable = { \ + LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ + _LO_IMPL_VTABLE, _ARG_impl_name) \ + } + +#define _LO_IMPL_PROTO( _ARG_impl_type, _ARG_impl_name, _ARG_quals, _tuple_typ, ...) _LO_IMPL_PROTO_##_tuple_typ(_ARG_impl_type, _ARG_impl_name, _ARG_quals, __VA_ARGS__) +#define _LO_IMPL_PROTO_lo_nest(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _ARG_child_iface_name) /* empty */ +#define _LO_IMPL_PROTO_lo_func(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _ARG_ret_type, _ARG_func_name, ...) _ARG_quals _ARG_ret_type _ARG_impl_name##_##_ARG_func_name(_ARG_impl_type * __VA_OPT__(,) __VA_ARGS__); + +#define _LO_IMPL_VTABLE(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) +#define _LO_IMPL_VTABLE_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE2, _ARG_impl_name) +#define _LO_IMPL_VTABLE_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, + +#define _LO_IMPL_VTABLE2(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE2_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) +#define _LO_IMPL_VTABLE2_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE3(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE3, _ARG_impl_name) +#define _LO_IMPL_VTABLE2_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, + +#define _LO_IMPL_VTABLE3(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE3_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) +#define _LO_IMPL_VTABLE3_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE4(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE4, _ARG_impl_name) +#define _LO_IMPL_VTABLE3_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, + +#define _LO_IMPL_VTABLE4(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE4_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) +#define _LO_IMPL_VTABLE4_lo_nest(_ARG_impl_name, _ARG_child_iface_name) static_assert(0, "BUG: libmisc/obj.h cannot nest interfaces more than 4 deep"); +#define _LO_IMPL_VTABLE4_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, + + +#endif /* _LIBMISC_OBJ_H_ */ diff --git a/libmisc/tests/test_obj.c b/libmisc/tests/test_obj.c new file mode 100644 index 0000000..687ad4e --- /dev/null +++ b/libmisc/tests/test_obj.c @@ -0,0 +1,61 @@ +/* libmisc/tests/test_obj.c - Tests for + * + * Copyright (C) 2024-2025 Luke T. Shumaker + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include + +#include "test.h" + +/* `lo_inteface frobber` header ***********************************************/ + +#define frobber_LO_IFACE \ + /** Basic function. */ \ + LO_FUNC(int, frob) \ + /** Function that takes 1 argument. */ \ + LO_FUNC(int, frob1, int) \ + /** Function that returns nothing. */ \ + LO_FUNC(void, frob0) +LO_INTERFACE(frobber); + +/* `struct myclass` header ****************************************************/ + +struct myclass { + int a; +}; +LO_IMPLEMENTATION_H(frobber, struct myclass, myclass); + +/* `struct myclass` implementation ********************************************/ + +LO_IMPLEMENTATION_C(frobber, struct myclass, myclass, static); + +static int myclass_frob(struct myclass *self) { + test_assert(self); + return self->a; +} + +static int myclass_frob1(struct myclass *self, int arg) { + test_assert(self); + return arg; +} + +static void myclass_frob0(struct myclass *self) { + test_assert(self); +} + +/* main test body *************************************************************/ + +#define MAGIC1 909837 +#define MAGIC2 657441 + +int main() { + struct myclass obj = { + .a = MAGIC1, + }; + 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); + return 0; +} diff --git a/libmisc/tests/test_obj_nest.c b/libmisc/tests/test_obj_nest.c new file mode 100644 index 0000000..bb9d6de --- /dev/null +++ b/libmisc/tests/test_obj_nest.c @@ -0,0 +1,73 @@ +/* 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 = {0}; + lo_interface read_writer obj = lo_box_myclass_as_read_writer(&_obj); + test_assert(LO_CALL(obj, write, "Hello", 6) == 6); + char buf[6] = {0}; + test_assert(LO_CALL(obj, read, buf, 3) == 3); + test_assert(memcmp(buf, "Hel\0\0\0", 6) == 0); + return 0; +} diff --git a/libobj/CMakeLists.txt b/libobj/CMakeLists.txt deleted file mode 100644 index f452e8a..0000000 --- a/libobj/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# libobj/CMakeLists.txt - A simple Go-ish object system built on GCC -fplan9-extensions -# -# Copyright (C) 2024-2025 Luke T. Shumaker -# SPDX-License-Identifier: AGPL-3.0-or-later - -add_library(libobj INTERFACE) -target_include_directories(libobj PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(libobj INTERFACE - libmisc -) - -add_lib_test(libobj test_obj) -add_lib_test(libobj test_nest) diff --git a/libobj/include/libobj/obj.h b/libobj/include/libobj/obj.h deleted file mode 100644 index 06b7298..0000000 --- a/libobj/include/libobj/obj.h +++ /dev/null @@ -1,173 +0,0 @@ -/* libobj/obj.h - A simple Go-ish object system - * - * Copyright (C) 2024-2025 Luke T. Shumaker - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _LIBOBJ_OBJ_H_ -#define _LIBOBJ_OBJ_H_ - -#include - -/** - * Use `lo_interface` similarly to how you would use - * `struct`/`enum`/`union` when writing the type of an interface - * value. - */ -#define lo_interface struct - -/** - * Use `LO_INTERFACE` in a .h file to define an interface. - * - * First define a macro named `{iface_name}_LO_IFACE` consisting of a - * series of calls to LO_NEST and/or LO_FUNC, then call - * `LO_INTERFACE({iface_name})`: - * - * #define myiface_LO_IFACE \ - * LO_NEST(wrapped_iface_name) \ - * LO_FUNC(ret_type, func_name, args...) - * LO_INTERFACE(myiface) - * - * 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) -#define LO_FUNC(_ARG_ret_type, _ARG_func_name, ...) \ - (lo_func, _ARG_ret_type, _ARG_func_name __VA_OPT__(,) __VA_ARGS__) -#define LO_INTERFACE(_ARG_iface_name) \ - struct _lo_##_ARG_iface_name##_vtable { \ - LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ - _LO_IFACE_VTABLE) \ - }; \ - struct _ARG_iface_name { \ - void *self; \ - const struct _lo_##_ARG_iface_name##_vtable *vtable; \ - }; \ - LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ - _LO_IFACE_PROTO, _ARG_iface_name) \ - extern int LM_CAT2_(_HIDDEN_BOGUS_, __COUNTER__) - -#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_VTABLE2(_tuple_typ, ...) _LO_IFACE_VTABLE2_##_tuple_typ(__VA_ARGS__) -#define _LO_IFACE_VTABLE2_lo_nest(_ARG_child_iface_name) const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; LM_FOREACH_TUPLE3(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE3) -#define _LO_IFACE_VTABLE2_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); - -#define _LO_IFACE_VTABLE3(_tuple_typ, ...) _LO_IFACE_VTABLE3_##_tuple_typ(__VA_ARGS__) -#define _LO_IFACE_VTABLE3_lo_nest(_ARG_child_iface_name) const struct _lo_##_ARG_child_iface_name##_vtable *_lo_##_ARG_child_iface_name##_vtable; LM_FOREACH_TUPLE4(_ARG_child_iface_name##_LO_IFACE, _LO_IFACE_VTABLE4) -#define _LO_IFACE_VTABLE3_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); - -#define _LO_IFACE_VTABLE4(_tuple_typ, ...) _LO_IFACE_VTABLE4_##_tuple_typ(__VA_ARGS__) -#define _LO_IFACE_VTABLE4_lo_nest(_ARG_child_iface_name) static_assert(0, "BUG: libobj cannot nest interfaces more than 4 deep"); -#define _LO_IFACE_VTABLE4_lo_func(_ARG_ret_type, _ARG_func_name, ...) _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __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_func(_ARG_iface_name, _ARG_ret_type, _ARG_func_name, ...) \ - /* empty */ - -/** - * `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){0}) - -/** - * `LO_IS_NULL(iface_val)` returns whether `iface_val` is LO_NULL. - */ -#define LO_IS_NULL(_ARG_iface_val) ((_ARG_iface_val).vtable == NULL) - -/** - * `LO_IFACE_EQ(a, b)` returns whether the interface values `a` and - * `b` are the same object. - */ -#define LO_EQ(_ARG_iface_val_a, _ARG_iface_val_b) \ - ((_ARG_iface_val_a).self == (_ARG_iface_val_b).self) - -/** - * Use LO_CALL(obj, method_name, args...) to call a method on an `lo_interface`. - */ -#define LO_CALL(_ARG_obj, _ARG_meth, ...) \ - (_ARG_obj).vtable->_ARG_meth((_ARG_obj).self __VA_OPT__(,) __VA_ARGS__) - -/** - * Use `LO_IMPLEMENTATION_H(iface_name, impl_type, impl_name)` in a .h - * 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)` - * function. - * - * 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_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, \ - .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable, \ - }; \ - } \ - LM_FORCE_SEMICOLON - -/** - * Use `LO_IMPLEMENTATION_C(iface_name, impl_type, impl_name[, static])` in a .c - * file to declare that `{impl_type}` implements the `{iface_name}` interface - * with functions named `{impl_name}_{method_name}`. - * - * You must also call the LO_IMPLEMENTATION_H in the corresponding .h file. - * - * If `iface_name` contains a nested interface, then the - * implementation of the nested interfaces must be declared with - * `LO_IMPLEMENTATION_C` first. - */ -#define LO_IMPLEMENTATION_C(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name, ...) \ - /* Method prototypes. */ \ - LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ - _LO_IMPL_PROTO, _ARG_impl_type, _ARG_impl_name, __VA_ARGS__) \ - /* Vtable. */ \ - const struct _lo_##_ARG_iface_name##_vtable \ - _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable = { \ - LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ - _LO_IMPL_VTABLE, _ARG_impl_name) \ - } - -#define _LO_IMPL_PROTO( _ARG_impl_type, _ARG_impl_name, _ARG_quals, _tuple_typ, ...) _LO_IMPL_PROTO_##_tuple_typ(_ARG_impl_type, _ARG_impl_name, _ARG_quals, __VA_ARGS__) -#define _LO_IMPL_PROTO_lo_nest(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _ARG_child_iface_name) /* empty */ -#define _LO_IMPL_PROTO_lo_func(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _ARG_ret_type, _ARG_func_name, ...) _ARG_quals _ARG_ret_type _ARG_impl_name##_##_ARG_func_name(_ARG_impl_type * __VA_OPT__(,) __VA_ARGS__); - -#define _LO_IMPL_VTABLE(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) -#define _LO_IMPL_VTABLE_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE2(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE2, _ARG_impl_name) -#define _LO_IMPL_VTABLE_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, - -#define _LO_IMPL_VTABLE2(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE2_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) -#define _LO_IMPL_VTABLE2_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE3(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE3, _ARG_impl_name) -#define _LO_IMPL_VTABLE2_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, - -#define _LO_IMPL_VTABLE3(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE3_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) -#define _LO_IMPL_VTABLE3_lo_nest(_ARG_impl_name, _ARG_child_iface_name) ._lo_##_ARG_child_iface_name##_vtable = &_lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, LM_FOREACH_TUPLE4(_ARG_child_iface_name##_LO_IFACE, _LO_IMPL_VTABLE4, _ARG_impl_name) -#define _LO_IMPL_VTABLE3_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, - -#define _LO_IMPL_VTABLE4(_ARG_impl_name, _tuple_typ, ...) _LO_IMPL_VTABLE4_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) -#define _LO_IMPL_VTABLE4_lo_nest(_ARG_impl_name, _ARG_child_iface_name) static_assert(0, "BUG: libobj cannot nest interfaces more than 4 deep"); -#define _LO_IMPL_VTABLE4_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, - - -#endif /* _LIBOBJ_OBJ_H_ */ diff --git a/libobj/tests/test.h b/libobj/tests/test.h deleted file mode 120000 index 2fb1bd5..0000000 --- a/libobj/tests/test.h +++ /dev/null @@ -1 +0,0 @@ -../../libmisc/tests/test.h \ No newline at end of file diff --git a/libobj/tests/test_nest.c b/libobj/tests/test_nest.c deleted file mode 100644 index f18b018..0000000 --- a/libobj/tests/test_nest.c +++ /dev/null @@ -1,73 +0,0 @@ -/* libobj/tests/test_nest.c - Tests for - * - * 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 = {0}; - lo_interface read_writer obj = lo_box_myclass_as_read_writer(&_obj); - test_assert(LO_CALL(obj, write, "Hello", 6) == 6); - char buf[6] = {0}; - test_assert(LO_CALL(obj, read, buf, 3) == 3); - test_assert(memcmp(buf, "Hel\0\0\0", 6) == 0); - return 0; -} diff --git a/libobj/tests/test_obj.c b/libobj/tests/test_obj.c deleted file mode 100644 index d6861dc..0000000 --- a/libobj/tests/test_obj.c +++ /dev/null @@ -1,61 +0,0 @@ -/* libobj/tests/test_obj.c - Tests for - * - * Copyright (C) 2024-2025 Luke T. Shumaker - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include - -#include "test.h" - -/* `lo_inteface frobber` header ***********************************************/ - -#define frobber_LO_IFACE \ - /** Basic function. */ \ - LO_FUNC(int, frob) \ - /** Function that takes 1 argument. */ \ - LO_FUNC(int, frob1, int) \ - /** Function that returns nothing. */ \ - LO_FUNC(void, frob0) -LO_INTERFACE(frobber); - -/* `struct myclass` header ****************************************************/ - -struct myclass { - int a; -}; -LO_IMPLEMENTATION_H(frobber, struct myclass, myclass); - -/* `struct myclass` implementation ********************************************/ - -LO_IMPLEMENTATION_C(frobber, struct myclass, myclass, static); - -static int myclass_frob(struct myclass *self) { - test_assert(self); - return self->a; -} - -static int myclass_frob1(struct myclass *self, int arg) { - test_assert(self); - return arg; -} - -static void myclass_frob0(struct myclass *self) { - test_assert(self); -} - -/* main test body *************************************************************/ - -#define MAGIC1 909837 -#define MAGIC2 657441 - -int main() { - struct myclass obj = { - .a = MAGIC1, - }; - 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); - return 0; -} -- cgit v1.2.3-2-g168b