summaryrefslogtreecommitdiff
path: root/src/util/callback.h
diff options
context:
space:
mode:
authorday <day@national.shitposting.agency>2026-03-16 16:25:49 +0100
committerday <day@national.shitposting.agency>2026-03-16 16:25:49 +0100
commit7f85c9fc75bd62ac09ea4457d3b17f85988fca66 (patch)
tree15248e42bfafc6bd19e50c9010b701057958ff3a /src/util/callback.h
parent872c39b24ecf4063f785ff3e8b2f940acd8c2d59 (diff)
parent991352b0d2767e6bd1a46f554db4ac9d208c13ad (diff)
Merge remote-tracking branch 'origin/master' into obj
Diffstat (limited to 'src/util/callback.h')
-rw-r--r--src/util/callback.h84
1 files changed, 84 insertions, 0 deletions
diff --git a/src/util/callback.h b/src/util/callback.h
new file mode 100644
index 0000000..a2c9477
--- /dev/null
+++ b/src/util/callback.h
@@ -0,0 +1,84 @@
+#pragma once
+#include "typedef.h"
+
+template <typename T>
+struct FN;
+// voodoo
+template <typename RET, typename... ARGS>
+struct FN<RET(ARGS...)> {
+ template <typename T> struct __strip_ref { using type = T; };
+ template <typename T> struct __strip_ref<T&> { using type = T; };
+ template <typename T> struct __strip_ref<T&&> { using type = T; };
+ template <typename R, typename... A> struct __strip_ref<R(A...)> { using type = R(*)(A...); };
+ template <typename R, typename... A> struct __strip_ref<R(&)(A...)> { using type = R(*)(A...); };
+
+ static const unsigned int BUF_SIZE = 32;
+
+ alignas(8) char buf[BUF_SIZE];
+ void* data;
+ RET( *invoke )( void*, ARGS... );
+ void( *destroy )( void* );
+ void*( *clone )( void*, char* );
+
+ template <typename F>
+ FN(F&& f) {
+ using __stripped = typename __strip_ref<F>::type;
+
+ if constexpr( sizeof(__stripped) <= BUF_SIZE ) {
+ new (buf) __stripped( f );
+ data = buf;
+ destroy = pfn( void* d ) {
+ ( (__stripped*)d )->~__stripped();
+ };
+ clone = pfn( void* d, char* dst ) -> void* {
+ new (dst) __stripped( *(__stripped*)d );
+ return dst;
+ };
+ } else {
+ data = new __stripped( f );
+ destroy = pfn( void* d ) {
+ delete (__stripped*)d;
+ };
+ clone = pfn( void* d, char* dst ) -> void* {
+ return new __stripped( *(__stripped*)d );
+ };
+ }
+
+ invoke = pfn( void* d, ARGS... args ) -> RET {
+ return ( *(__stripped*)d )( args... );
+ };
+ }
+
+ FN( I32&& ) : data( 0 ), invoke( 0 ), destroy( 0 ), clone( 0 ) {}
+ FN( U32&& ) : data( 0 ), invoke( 0 ), destroy( 0 ), clone( 0 ) {}
+ FN() : data( 0 ), invoke( 0 ), destroy( 0 ), clone( 0 ) {}
+
+ FN( const FN& other ) : invoke( other.invoke ), destroy( other.destroy ), clone( other.clone ) {
+ if( !other.data ) {
+ data = 0;
+ return;
+ }
+
+ data = other.clone( other.data, buf );
+ }
+
+ FN& operator=( const FN& other ) {
+ if( this == &other ) return *this;
+ if( destroy && data ) destroy( data );
+ invoke = other.invoke;
+ destroy = other.destroy;
+ clone = other.clone;
+ data = other.data ? other.clone( other.data, buf ) : 0;
+ return *this;
+ }
+
+ ~FN() {
+ if( destroy && data ) destroy( data );
+ }
+
+ RET operator()( ARGS... args ) const {
+ return invoke( data, args... );
+ }
+
+ operator bool() const { return !!invoke; }
+};