#include #include // Information Interface template class __FunctionInfo { typedef Ret (*type)(Args...); public: [[nodiscard]] virtual bool can_overwrite() const = 0; [[nodiscard]] virtual type get() const = 0; [[nodiscard]] virtual type *get_addr() const = 0; virtual void update(type new_func) = 0; }; // Thunks typedef void *(*thunk_enabler_t)(void *target, void *thunk); extern thunk_enabler_t thunk_enabler; // Function template class __Function; template class __Function final { // Prevent Copying __PREVENT_COPY(__Function); __PREVENT_DESTRUCTION(__Function); // Instance static __Function *instance; // Current Function typedef __FunctionInfo *func_t; const func_t func; public: // Types typedef Ret (*ptr_type)(Args...); typedef std::function type; typedef std::function overwrite_type; // State const bool enabled; const char *const name; // Backup Of Original Function Pointer const ptr_type backup; #ifdef {{ BUILDING_SYMBOLS_GUARD }} // Constructor __Function(const char *const name_, const func_t func_): func(func_), enabled(func->can_overwrite()), name(name_), backup(func->get()) { instance = this; } #else // Prevent Construction __PREVENT_JUST_CONSTRUCTION(__Function); #endif // Overwrite Function [[nodiscard]] bool overwrite(const overwrite_type &target) { // Check If Enabled if (!enabled) { return false; } // Enable Thunk enable_thunk(); // Overwrite type original = get_thunk_target(); thunk_target = [original, target](Args... args) { return target(original, std::forward(args)...); }; return true; } // Getters [[nodiscard]] ptr_type get(const bool result_will_be_stored) { if (!enabled) { return nullptr; } else { if (result_will_be_stored) { enable_thunk(); } return func->get(); } } [[nodiscard]] ptr_type *get_vtable_addr() const { return func->get_addr(); } private: // Thunk [[nodiscard]] type get_thunk_target() const { if (thunk_target) { return thunk_target; } else { return backup; } } static Ret thunk(Args... args) { return instance->get_thunk_target()(std::forward(args)...); } // Enable Thunk type thunk_target; bool thunk_enabled = false; void enable_thunk() { if (!thunk_enabled && enabled) { ptr_type real_thunk = (ptr_type) thunk_enabler((void *) backup, (void *) thunk); func->update(real_thunk); thunk_enabled = true; } } };