From ea35c76ff3022c825d61fb91d285ef3b67fdd527 Mon Sep 17 00:00:00 2001 From: matcool <26722564+matcool@users.noreply.github.com> Date: Fri, 31 Jan 2025 23:01:53 -0300 Subject: [PATCH] wip add macros for exporting functions via events --- loader/include/Geode/loader/Dispatch.hpp | 92 ++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 5 deletions(-) diff --git a/loader/include/Geode/loader/Dispatch.hpp b/loader/include/Geode/loader/Dispatch.hpp index 629ede393..485095d14 100644 --- a/loader/include/Geode/loader/Dispatch.hpp +++ b/loader/include/Geode/loader/Dispatch.hpp @@ -16,11 +16,11 @@ namespace geode { protected: std::string m_id; std::tuple m_args; - + public: - DispatchEvent(std::string const& id, Args... args) - : m_id(id), m_args(std::make_tuple(args...)) {} - + DispatchEvent(std::string const& id, Args... args) : + m_id(id), m_args(std::make_tuple(args...)) {} + std::tuple getArgs() const { return m_args; } @@ -61,6 +61,88 @@ namespace geode { } DispatchFilter(std::string const& id) : m_id(id) {} + DispatchFilter(DispatchFilter const&) = default; }; -} \ No newline at end of file +} + +// - Macros for exporting functions via events - + +// You can use these to easily export functions to other mods +// without being a required depedency. +// # Example Usage: +/* +``` +// (In your api distributed header file) +#pragma once + +#include +// You must **manually** declare the mod id, as macros like GEODE_MOD_ID will not +// behave correctly to other mods using your api. +#define MY_MOD_ID "dev.my-api" + +namespace api { +// Important: The function must be declared inline, and return a geode::Result, +// as it can fail if the api is not available. +inline geode::Result addNumbers(int a, int b) GEODE_EVENT_EXPORT(&addNumbers, (a, b)); +} +``` +*/ +// Then, in **one** of your source files, you must define the exported functions: +/* +``` +// MUST be defined before including the header. +#define GEODE_DEFINE_EVENT_EXPORTS +#include "../include/api.hpp" + +Result api::addNumbers(int a, int b) { + return Ok(a + b); +} +``` +*/ + +// once this is set in stone we should not change it ever +#define GEODE_EVENT_EXPORT_ID_FOR(fnPtrStr, callArgsStr) \ + (std::string(MY_MOD_ID "/") + (fnPtrStr[0] == '&' ? (fnPtrStr + 1) : fnPtrStr)) + +#define GEODE_EVENT_EXPORT_CALL(fnPtr, callArgs, eventID) \ + { \ + static decltype(fnPtr) _functionStorage = [] { \ + decltype(fnPtr) ptr = nullptr; \ + geode::DispatchEvent(eventID, &ptr).post(); \ + return ptr; \ + }(); \ + if (!_functionStorage) return geode::Err("Unable to call method"); \ + return _functionStorage callArgs; \ + } + +#define GEODE_EVENT_EXPORT_DEFINE(fnPtr, callArgs, eventID) \ + ; \ + $execute { \ + using Type = decltype(fnPtr); \ + new geode::EventListener( \ + +[](Type* ptr) { \ + *ptr = fnPtr; \ + return geode::ListenerResult::Stop; \ + }, \ + geode::DispatchFilter(eventID) \ + ); \ + } + +#ifndef GEODE_DEFINE_EVENT_EXPORTS + + #define GEODE_EVENT_EXPORT(fnPtr, callArgs) \ + GEODE_EVENT_EXPORT_CALL(fnPtr, callArgs, GEODE_EVENT_EXPORT_ID_FOR(#fnPtr, #callArgs)) + + #define GEODE_EVENT_EXPORT_ID(fnPtr, callArgs, eventID) \ + GEODE_EVENT_EXPORT_CALL(fnPtr, callArgs, eventID) + +#else + + #define GEODE_EVENT_EXPORT(fnPtr, callArgs) \ + GEODE_EVENT_EXPORT_DEFINE(fnPtr, callArgs, GEODE_EVENT_EXPORT_ID_FOR(#fnPtr, #callArgs)) + + #define GEODE_EVENT_EXPORT_ID(fnPtr, callArgs, eventID) \ + GEODE_EVENT_EXPORT_DEFINE(fnPtr, callArgs, eventID) + +#endif \ No newline at end of file