summaryrefslogtreecommitdiff
path: root/libobj/include/libobj/obj.h
blob: d8a528a079027ae260ee0daa94c436ba86a3a539 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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_ */