summaryrefslogtreecommitdiff
path: root/libmisc
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-26 19:51:52 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-06 11:50:46 -0600
commit1cf085ae086a43f18af550fdcfd4d3e57ccb0918 (patch)
tree42abde5f3b34b239bb95e84200ea16ad4a4c562d /libmisc
parent0fec22d4106ff6f80296d1511eec7a82160c2245 (diff)
parenta83c95e9f46ef695a55fc7a6911e11846da9903c (diff)
Merge branch 'lukeshu/misc'
Diffstat (limited to 'libmisc')
-rw-r--r--libmisc/CMakeLists.txt3
-rw-r--r--libmisc/assert.c12
-rw-r--r--libmisc/include/libmisc/log.h16
-rw-r--r--libmisc/include/libmisc/macro.h19
-rw-r--r--libmisc/include/libmisc/map.h1
-rw-r--r--libmisc/include/libmisc/obj.h173
-rw-r--r--libmisc/include/libmisc/private.h4
-rw-r--r--libmisc/tests/test_assert.c1
-rw-r--r--libmisc/tests/test_log.c10
-rw-r--r--libmisc/tests/test_obj.c61
-rw-r--r--libmisc/tests/test_obj_nest.c73
-rw-r--r--libmisc/tests/test_rand.c3
12 files changed, 349 insertions, 27 deletions
diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt
index fdbe949..8c9fa57 100644
--- a/libmisc/CMakeLists.txt
+++ b/libmisc/CMakeLists.txt
@@ -12,7 +12,6 @@ target_sources(libmisc INTERFACE
log.c
map.c
)
-target_compile_options(libmisc INTERFACE "$<$<COMPILE_LANGUAGE:C>:-fplan9-extensions>")
add_lib_test(libmisc test_assert)
add_lib_test(libmisc test_assert_min)
@@ -21,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/assert.c b/libmisc/assert.c
index fdd8154..540d2fd 100644
--- a/libmisc/assert.c
+++ b/libmisc/assert.c
@@ -4,10 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdbool.h> /* for bool, true, false */
-
#define LOG_NAME ASSERT
-#include <libmisc/log.h> /* for errorf() */
+#include <libmisc/log.h> /* for log_errorf() */
#include <libmisc/assert.h>
@@ -19,10 +17,10 @@ void __assert_msg_fail(const char *expr,
static bool in_fail = false;
if (!in_fail) {
in_fail = true;
- errorf("%s:%u:%s(): assertion \"%s\" failed%s%s",
- file, line, func,
- expr,
- msg ? ": " : "", msg ?: "");
+ log_errorf("%s:%u:%s(): assertion \"%s\" failed%s%s",
+ file, line, func,
+ expr,
+ msg ? ": " : "", msg ?: "");
in_fail = false;
}
__lm_abort();
diff --git a/libmisc/include/libmisc/log.h b/libmisc/include/libmisc/log.h
index 79c0ab6..4529946 100644
--- a/libmisc/include/libmisc/log.h
+++ b/libmisc/include/libmisc/log.h
@@ -20,15 +20,15 @@
const char *const_byte_str(uint8_t b);
-#define n_errorf(nam, fmt, ...) do { __lm_printf("error: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
-#define n_infof(nam, fmt, ...) do { __lm_printf("info : " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
-#define n_debugf(nam, fmt, ...) do { if (LM_CAT3_(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \
- __lm_printf("debug: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
+#define log_n_errorf(nam, fmt, ...) do { __lm_printf("error: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
+#define log_n_infof(nam, fmt, ...) do { __lm_printf("info : " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
+#define log_n_debugf(nam, fmt, ...) do { if (LM_CAT3_(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \
+ __lm_printf("debug: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0)
#endif /* _LIBMISC_LOG_H_ */
-#if defined(LOG_NAME) && !defined(errorf)
-#define errorf(fmt, ...) n_errorf(LOG_NAME, fmt, __VA_ARGS__)
-#define infof(fmt, ...) n_infof(LOG_NAME, fmt, __VA_ARGS__)
-#define debugf(fmt, ...) n_debugf(LOG_NAME, fmt, __VA_ARGS__)
+#if defined(LOG_NAME) && !defined(log_errorf)
+#define log_errorf(fmt, ...) log_n_errorf(LOG_NAME, fmt, __VA_ARGS__)
+#define log_infof(fmt, ...) log_n_infof(LOG_NAME, fmt, __VA_ARGS__)
+#define log_debugf(fmt, ...) log_n_debugf(LOG_NAME, fmt, __VA_ARGS__)
#endif
diff --git a/libmisc/include/libmisc/macro.h b/libmisc/include/libmisc/macro.h
index a95ac82..ae204ae 100644
--- a/libmisc/include/libmisc/macro.h
+++ b/libmisc/include/libmisc/macro.h
@@ -9,6 +9,8 @@
#include <libmisc/assert.h>
+#define LM_FORCE_SEMICOLON static_assert(1, "force semicolon")
+
/* for function definitions */
#define LM_UNUSED(argname)
@@ -108,4 +110,21 @@
#define _LM_EVAL__2(...) _LM_EVAL__1(_LM_EVAL__1(__VA_ARGS__))
#define _LM_EVAL__1(...) __VA_ARGS__
+/** The same as LM_FOREACH_TUPLE(), but callable from inside of LM_FOREACH_TUPLE(). */
+#define LM_FOREACH_TUPLE2(tuples, func, ...) \
+ LM_IF(LM_TUPLES_NONEMPTY(tuples))( \
+ _LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \
+ _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \
+ )()
+#define LM_FOREACH_TUPLE3(tuples, func, ...) \
+ LM_IF(LM_TUPLES_NONEMPTY(tuples))( \
+ _LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \
+ _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \
+ )()
+#define LM_FOREACH_TUPLE4(tuples, func, ...) \
+ LM_IF(LM_TUPLES_NONEMPTY(tuples))( \
+ _LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \
+ _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \
+ )()
+
#endif /* _LIBMISC_MACRO_H_ */
diff --git a/libmisc/include/libmisc/map.h b/libmisc/include/libmisc/map.h
index 6622595..113bc0e 100644
--- a/libmisc/include/libmisc/map.h
+++ b/libmisc/include/libmisc/map.h
@@ -7,7 +7,6 @@
#ifndef _LIBMISC_MAP_H_
#define _LIBMISC_MAP_H_
-#include <stdbool.h>
#include <stddef.h> /* for size_t */
#include <stdint.h> /* for uint8_t */
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 <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBMISC_OBJ_H_
+#define _LIBMISC_OBJ_H_
+
+#include <libmisc/macro.h>
+
+/**
+ * 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/include/libmisc/private.h b/libmisc/include/libmisc/private.h
index 5518d1f..5a8777c 100644
--- a/libmisc/include/libmisc/private.h
+++ b/libmisc/include/libmisc/private.h
@@ -11,7 +11,7 @@
#define YES LM_SENTINEL()
#define IS_IMPLEMENTATION_FOR(name) LM_IS_SENTINEL(IMPLEMENTATION_FOR_##name)
-#define BEGIN_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(struct {) struct {} LM_CAT2_(_PRIVATE_FORCE_SEMICOLON_, __COUNTER__)
-#define END_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))(struct {} LM_CAT2_(_PRIVATE_FORCE_SEMICOLON_, __COUNTER__))(} LM_CAT2_(_PRIVATE_, __COUNTER__))
+#define BEGIN_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(struct {) LM_FORCE_SEMICOLON
+#define END_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))(LM_FORCE_SEMICOLON)(} LM_CAT2_(_PRIVATE_, __COUNTER__))
#endif /* _LIBMISC_PRIVATE_H_ */
diff --git a/libmisc/tests/test_assert.c b/libmisc/tests/test_assert.c
index 15f9446..c6d2dc1 100644
--- a/libmisc/tests/test_assert.c
+++ b/libmisc/tests/test_assert.c
@@ -6,7 +6,6 @@
#include <setjmp.h>
#include <stdarg.h> /* for va_list, va_start(), va_end() */
-#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
diff --git a/libmisc/tests/test_log.c b/libmisc/tests/test_log.c
index 49a76ca..02b95d6 100644
--- a/libmisc/tests/test_log.c
+++ b/libmisc/tests/test_log.c
@@ -1,6 +1,6 @@
/* libmisc/tests/test_log.c - Tests for <libmisc/log.h>
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -54,17 +54,17 @@ int __lm_printf(const char *format, ...) {
int main() {
should_print("error: FROBNICATE: val=42\n",
- errorf("val=%d", 42));
+ log_errorf("val=%d", 42));
should_print("info : FROBNICATE: val=0\n",
- infof("val=%d", 0));
+ log_infof("val=%d", 0));
#ifndef NDEBUG
#define CONFIG_FROBNICATE_DEBUG 1
should_print("debug: FROBNICATE: val=-2\n",
- debugf("val=%d", -2));
+ log_debugf("val=%d", -2));
#undef CONFIG_FROBNICATE_DEBUG
#define CONFIG_FROBNICATE_DEBUG 0
should_print(NULL,
- debugf("val=%d", -2));
+ log_debugf("val=%d", -2));
#endif
return 0;
}
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 <libmisc/obj.h>
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/obj.h>
+
+#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 <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 = {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/libmisc/tests/test_rand.c b/libmisc/tests/test_rand.c
index 8076155..3596d94 100644
--- a/libmisc/tests/test_rand.c
+++ b/libmisc/tests/test_rand.c
@@ -1,10 +1,9 @@
/* libmisc/tests/test_rand.c - Tests for <libmisc/rand.h>
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdbool.h>
#include <setjmp.h>
#include <libmisc/rand.h>