summaryrefslogtreecommitdiffhomepage
path: root/src/vcall.h
diff options
context:
space:
mode:
authorMichael Smith <mikesmiffy128@gmail.com>2022-09-13 21:46:21 +0100
committerMichael Smith <mikesmiffy128@gmail.com>2022-09-13 22:50:30 +0100
commit792463cb133f65645feb48743bbc03ef8eb96bdd (patch)
tree3879cd6895fe5c8f597f445af2410a41bcf00f73 /src/vcall.h
parent378892d089b6742a95b8d573af58535597f19d25 (diff)
Move towards C23, improve events and vcall macros
Another big one. Here's a list of things: - Since the upcoming C23 standardises typeof(), use it as an extension for the time being in order to allow passing arbitrary types as macro/codegen parameters. It wouldn't have been a big leap to do this even without standardisation since it's apparently an easy extension to implement - and also, to be honest, this project is essentially glued to Clang anyway so who cares. - Likewise, bool, true and false are becoming pre-defined, so pre-pre-define them now in order to get the benefit of not having to remember one header everywhere. - Really ungodly/amazing vcall macro stuff now allows us to call C++ virtual functions like regular C functions. It's pretty cool! - Events can now take arbitrary parameters and come in two types: regular events and predicates. All this makes the base code even uglier but makes the feature implementation nicer. In other words, it places more of the cognitive burden on myself and less on other people who might want to contribute. This is a good tradeoff, because I'm a genius.
Diffstat (limited to 'src/vcall.h')
-rw-r--r--src/vcall.h98
1 files changed, 87 insertions, 11 deletions
diff --git a/src/vcall.h b/src/vcall.h
index caee70a..778ba09 100644
--- a/src/vcall.h
+++ b/src/vcall.h
@@ -35,32 +35,108 @@
#define VCALLCONV
#endif
+// black magic argument list maker thingmy, similar to the PPMAGIC_MAP thing,
+// but slightly different, so we get to have all this nonsense twice. not sorry
+
+// note: arg numbering is counting down instead of up because it's easier to
+// treat the vararg macros like a stack, and doesn't actually matter otherwise
+// also note: I just did 16. that should be enough
+#define _VCALL_ARG00()
+#define _VCALL_ARG01(t) typeof(t) a1
+#define _VCALL_ARG02(t1, t2) typeof(t1) a2, typeof(t2) a1
+#define _VCALL_ARG03(t1, t2, t3) typeof(t1) a3, typeof(t2) a2, typeof(t3) a1
+#define _VCALL_ARG04(t1, t2, t3, t4) \
+ typeof(t1) a4, typeof(t2) a3, typeof(t3) a2, typeof(t4) a1
+#define _VCALL_ARG05(t, ...) typeof(t) a5, _VCALL_ARG04(__VA_ARGS__)
+#define _VCALL_ARG06(t, ...) typeof(t) a6, _VCALL_ARG05(__VA_ARGS__)
+#define _VCALL_ARG07(t, ...) typeof(t) a7, _VCALL_ARG06(__VA_ARGS__)
+#define _VCALL_ARG08(t, ...) typeof(t) a8, _VCALL_ARG07(__VA_ARGS__)
+#define _VCALL_ARG09(t, ...) typeof(t) a9, _VCALL_ARG08(__VA_ARGS__)
+#define _VCALL_ARG10(t, ...) typeof(t) a10, _VCALL_ARG09(__VA_ARGS__)
+#define _VCALL_ARG11(t, ...) typeof(t) a11, _VCALL_ARG10(__VA_ARGS__)
+#define _VCALL_ARG12(t, ...) typeof(t) a12, _VCALL_ARG11(__VA_ARGS__)
+#define _VCALL_ARG13(t, ...) typeof(t) a13, _VCALL_ARG12(__VA_ARGS__)
+#define _VCALL_ARG14(t, ...) typeof(t) a14, _VCALL_ARG14(__VA_ARGS__)
+#define _VCALL_ARG15(t, ...) typeof(t) a15, _VCALL_ARG15(__VA_ARGS__)
+#define _VCALL_ARG16(t, ...) typeof(t) a16, _VCALL_ARG16(__VA_ARGS__)
+
+#define _VCALL_ARG_N(x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, \
+ x11, x12, x13, x14, x15, x16, N, ...) \
+ _VCALL_ARG##N
+
+#define _VCALL_ARGLIST(...) \
+ _VCALL_ARG_N(__VA_ARGS__ __VA_OPT__(,) \
+ 16, 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01, 00) \
+ (__VA_ARGS__)
+
+// aannd we need these as well...
+#define _VCALL_PASS00()
+#define _VCALL_PASS01() a1
+#define _VCALL_PASS02() a2, a1
+#define _VCALL_PASS03() a3, a2, a1
+#define _VCALL_PASS04() a4, a3, a2, a1
+#define _VCALL_PASS05() a5, _VCALL_PASS04()
+#define _VCALL_PASS06() a6, _VCALL_PASS05()
+#define _VCALL_PASS07() a7, _VCALL_PASS06()
+#define _VCALL_PASS08() a8, _VCALL_PASS07()
+#define _VCALL_PASS09() a9, _VCALL_PASS08()
+#define _VCALL_PASS10() a10, _VCALL_PASS09()
+#define _VCALL_PASS11() a11, _VCALL_PASS10()
+#define _VCALL_PASS12() a12, _VCALL_PASS11()
+#define _VCALL_PASS13() a13, _VCALL_PASS12()
+#define _VCALL_PASS14() a14, _VCALL_PASS13()
+#define _VCALL_PASS15() a15, _VCALL_PASS14()
+#define _VCALL_PASS16() a16, _VCALL_PASS15()
+#define _VCALL_PASS_N(x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, \
+ x12, x13, x14, x15, x16, N, ...) \
+ _VCALL_PASS##N
+
+#define _VCALL_PASSARGS(...) \
+ _VCALL_PASS_N(__VA_ARGS__ __VA_OPT__(,) 16, 15, 14, 13, 12, 11, 10, 09, \
+ 08, 07, 06, 05, 04, 03, 02, 01, 00)()
+
+#define VFUNC(x, name) ((*(name##_func **)(x))[vtidx_##name])
+#define VCALL(x, name, ...) VFUNC(x, name)(x, ##__VA_ARGS__)
+
+// even more magic: return keyword only if not void
+#define _VCALL_RETKW_(x, n, ...) n
+#define _VCALL_RETKW(...) _VCALL_RETKW_(__VA_ARGS__, return,)
+#define _VCALL_RET_void() x, ,
+#define _VCALL_RET(t) _VCALL_RETKW(_VCALL_RET_##t())
+
+// I thought static inline was supposed to prevent unused warnings???
+#if defined(__GNUC__) || defined(__clang__)
+#define _VCALL_UNUSED __attribute__((unused))
+#else
+#define _VCALL_UNUSED
+#endif
+
#define _DECL_VFUNC_DYN(ret, conv, name, ...) \
- /* XXX: GCC extension, seems worthwhile vs having two macros for one thing.
- Replace with __VA_OPT__(,) whenever that gets fully standardised. */ \
- typedef ret (*conv name##_func)(void *this, ##__VA_ARGS__);
+ typedef typeof(ret) (*conv name##_func)(void * __VA_OPT__(,) __VA_ARGS__); \
+ static inline _VCALL_UNUSED typeof(ret) name(void *this __VA_OPT__(,) \
+ _VCALL_ARGLIST(__VA_ARGS__)) { \
+ _VCALL_RET(ret) VCALL(this, name __VA_OPT__(,) \
+ _VCALL_PASSARGS(__VA_ARGS__)); \
+ }
#define _DECL_VFUNC(ret, conv, name, idx, ...) \
enum { vtidx_##name = (idx) }; \
- _DECL_VFUNC_DYN(ret, conv, name, ##__VA_ARGS__)
+ _DECL_VFUNC_DYN(ret, conv, name __VA_OPT__(,) __VA_ARGS__)
/* Define a virtual function with a known index */
#define DECL_VFUNC(ret, name, idx, ...) \
- _DECL_VFUNC(ret, VCALLCONV, name, idx, ##__VA_ARGS__)
+ _DECL_VFUNC(ret, VCALLCONV, name, idx __VA_OPT__(,) __VA_ARGS__)
/* Define a virtual function with a known index, without thiscall convention */
#define DECL_VFUNC_CDECL(ret, name, idx, ...) \
- _DECL_VFUNC(ret, , name, idx, ##__VA_ARGS__)
+ _DECL_VFUNC(ret, , name, idx __VA_OPT__(,) __VA_ARGS__)
/* Define a virtual function with an index defined elsewhere */
#define DECL_VFUNC_DYN(ret, name, ...) \
- _DECL_VFUNC_DYN(ret, VCALLCONV, name, ##__VA_ARGS__)
+ _DECL_VFUNC_DYN(ret, VCALLCONV, name __VA_OPT__(,) __VA_ARGS__)
/* Define a virtual function with an index defined elsewhere, without thiscall */
#define DECL_VFUNC_CDECLDYN(ret, name, ...) \
- _DECL_VFUNC_DYN(ret, , name, ##__VA_ARGS__)
-
-#define VFUNC(x, name) ((*(name##_func **)(x))[vtidx_##name])
-#define VCALL(x, name, ...) VFUNC(x, name)(x, ##__VA_ARGS__)
+ _DECL_VFUNC_DYN(ret, , name __VA_OPT__(,) __VA_ARGS__)
#endif