summaryrefslogtreecommitdiff
path: root/libobj/include
diff options
context:
space:
mode:
Diffstat (limited to 'libobj/include')
-rw-r--r--libobj/include/libobj/obj.h154
1 files changed, 154 insertions, 0 deletions
diff --git a/libobj/include/libobj/obj.h b/libobj/include/libobj/obj.h
new file mode 100644
index 0000000..d8a528a
--- /dev/null
+++ b/libobj/include/libobj/obj.h
@@ -0,0 +1,154 @@
+/* libobj/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 _LIBOBJ_OBJ_H_
+#define _LIBOBJ_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) \
+ typedef struct { \
+ LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
+ _LO_IFACE_VTABLE) \
+ } _lo_##_ARG_iface_name##_vtable; \
+ struct _ARG_iface_name { \
+ void *self; \
+ const _lo_##_ARG_iface_name##_vtable *vtable; \
+ }; \
+ LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \
+ _LO_IFACE_PROTO, _ARG_iface_name)
+#define _LO_IFACE_VTABLE(_tuple_typ, ...) \
+ _LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__)
+#define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) \
+ _lo_##_ARG_child_iface_name##_vtable;
+#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_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 _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, \
+ }; \
+ }
+
+/**
+ * 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 _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,
+#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,
+
+#endif /* _LIBOBJ_OBJ_H_ */