summaryrefslogtreecommitdiff
path: root/libmisc/include
diff options
context:
space:
mode:
Diffstat (limited to 'libmisc/include')
-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
5 files changed, 202 insertions, 11 deletions
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_ */