summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig2
-rw-r--r--GNUmakefile4
-rw-r--r--cmd/sbc_harness/main.c10
-rw-r--r--lib9p/tests/test_server/main.c10
-rw-r--r--lib9p_util/include/util9p/static.h16
-rw-r--r--libmisc/CMakeLists.txt1
-rw-r--r--libmisc/include/libmisc/fmt.h52
-rw-r--r--libmisc/include/libmisc/obj.h61
-rw-r--r--libmisc/tests/test_obj.c2
-rw-r--r--libmisc/tests/test_obj_autobox.c83
-rwxr-xr-xlibmisc/tests/test_obj_autobox.c.gen17
-rw-r--r--libmisc/tests/test_obj_nest.c11
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;
}