Files
Volkan/source/examples/HotReloadDemo/Shared/PluginApi.h
T
2026-11-03 15:22:24 -03:00

104 lines
3.9 KiB
C

/**
* @file PluginApi.h
* @brief The contract PluginV1 and PluginV2 both implement,
* and the Host loads by name. Every function here follows
* cpp-style-guide.md section 11's rule for anything loaded at runtime:
* plain C linkage, no classes or exceptions crossing the boundary.
*
* This header is deliberately identical for both plugin versions --
* in a real project this is the file that stays stable while
* PluginV1/src/Plugin.cpp and PluginV2/src/Plugin.cpp (standing in for
* "the same file, before and after your edit") diverge.
*/
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Scales its input, and prints which build it is.
*
* Changes behavior between V1 and V2 -- watch this one get
* HOT_RELOAD_SYMBOL_PATCHED.
*
* @param input The value to scale.
* @return V1: `input * 2`. V2: `input * 3`.
*/
int plugin_tick(int input);
/**
* @brief A feature that exists only in V1.
*
* Gone in V2 -- watch this one get HOT_RELOAD_SYMBOL_REMOVED, and the
* Host's stub take over for any call still aimed at it afterwards.
*
* @param input The value to echo.
* @return @p input, unchanged.
*/
int plugin_legacy_feature(int input);
/**
* @brief A feature that exists only in V2.
*
* Watch this one get HOT_RELOAD_SYMBOL_ADDED, then get called for the
* first time right after.
*
* @param input The value to scale.
* @return `input * 10`.
*/
int plugin_new_feature(int input);
#if defined(__GNUC__) || defined(__clang__)
/**
* @brief An empty function, too small to hold the jump this library writes.
*
* Its patchable-function-entry padding is overridden down to zero, so
* all that is left is the compiled empty body -- a handful of bytes,
* fewer than the jump any of this library's backends writes on x86-64
* and ARM64 at any optimization level. Watch this one get
* HOT_RELOAD_SYMBOL_SKIPPED_TOO_SMALL rather than corrupt whatever comes
* after it.
*
* Two configurations cannot promise "too small": unoptimized 32-bit x86
* (its jump is only 5 bytes, which even an empty unoptimized function
* reaches), and Windows, where a PE image carries no per-function size
* and the library can only estimate one from the distance to the next
* export. MSVC has no equivalent per-function override this demo could
* apply, so this one only exists on GCC/Clang.
*
* ARM32 needs a different implementation underneath this same
* declaration (see PluginV1/src/Plugin.cpp): an ordinary empty C++
* body there, even with patchable-function-entry overridden to zero,
* still picks up an unoptimized build's full frame-pointer prologue
* and epilogue -- confirmed directly, 8 bytes on this architecture at
* `-O0`, tying this library's own ARM-state jump size exactly rather
* than falling safely under it. A single hand-written `bx lr`, at file
* scope rather than inside a C++ function body (sidestepping a
* separate GCC quirk: an asm statement inside a naked function body
* is followed by an unconditional extra NOP, confirmed directly),
* reliably stays under every ARM32 jump size this library ever
* writes, in both instruction sets, at every optimization level.
*/
__attribute__((patchable_function_entry(0))) void plugin_tiny_function(void);
#endif
/**
* @brief The build's version number: 1 in V1, 2 in V2.
*
* An exported VARIABLE, not a function, sitting right next to
* real functions in the same library and changing value between
* V1 and V2 just like plugin_tick's behavior does -- watch this
* one get HOT_RELOAD_SYMBOL_SKIPPED_NOT_CODE. This is the
* mechanical proof behind hot_reload.h's "only executable code
* changes" guarantee: nothing here tells the library "this one is
* data, don't touch it" -- it discovers that itself from the
* symbol table, the same way it discovers plugin_tick is code.
*/
extern int plugin_build_marker;
#ifdef __cplusplus
}
#endif