Fix documentation
This commit is contained in:
1 parent
a317c07124
commit
ab21b3f30d
27 files changed
+2137
-1043
No files matched your search
@@ -1,4 +1,6 @@
|
||||
/* Main.cpp -- loads PluginV1, calls into it, reloads to PluginV2, and
|
||||
/**
|
||||
* @file Main.cpp
|
||||
* @brief Scripted, deterministic demo: loads PluginV1, calls into it, reloads to PluginV2, and
|
||||
* calls the SAME already-resolved function pointers again to show
|
||||
* they now run the new code -- without the Host re-resolving anything.
|
||||
* Along the way this exercises every HotReloadSymbolEvent kind: watch
|
||||
@@ -17,33 +19,40 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
/// The signature every plain-C export in PluginApi.h shares.
|
||||
using PluginFunction = int (*)(int);
|
||||
|
||||
// CppWidget::tick's real ABI: an implicit this-pointer followed by
|
||||
// its one declared parameter, same calling convention the
|
||||
// compiler itself would generate for widget->tick(x) -- this is
|
||||
// just spelling that out explicitly so it can be called through a
|
||||
// manually-resolved address instead.
|
||||
/// CppWidget::tick's real ABI: an implicit this-pointer followed by
|
||||
/// its one declared parameter, same calling convention the
|
||||
/// compiler itself would generate for widget->tick(x) -- this is
|
||||
/// just spelling that out explicitly so it can be called through a
|
||||
/// manually-resolved address instead.
|
||||
using CppWidgetTickFunction = int (*)(CppWidget*, int);
|
||||
|
||||
/// Stands in for @c plugin_legacy_feature once V2 removes it.
|
||||
/// @return Always -1.
|
||||
int hostSideDeletedFunctionStub(int)
|
||||
{
|
||||
std::printf(" !! called a deleted plugin function -- this is the Host's stub, not a crash !!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Runs once per HOT_RELOAD_SYMBOL_REMOVED, before onSymbolEvent
|
||||
* below sees the same event -- see hot_reload.h's doc comment on
|
||||
* HotReloadDeletedSymbolFn. Redirecting a removed function to a
|
||||
* stub living in the HOST executable (not in either plugin build)
|
||||
* demonstrates that the target of a patch can be anywhere at all,
|
||||
* not just inside the module being reloaded. */
|
||||
/// Runs once per HOT_RELOAD_SYMBOL_REMOVED, before onSymbolEvent
|
||||
/// below sees the same event -- see hot_reload.h's doc comment on
|
||||
/// HotReloadDeletedSymbolFn. Redirecting a removed function to a
|
||||
/// stub living in the HOST executable (not in either plugin build)
|
||||
/// demonstrates that the target of a patch can be anywhere at all,
|
||||
/// not just inside the module being reloaded.
|
||||
/// @param symbolName The removed function's real (possibly mangled) name.
|
||||
/// @return The host stub to redirect old callers to.
|
||||
void* onDeletedSymbol(const char* symbolName, void* /*userData*/)
|
||||
{
|
||||
std::printf(" [event] REMOVED %-22s -> redirecting old callers to the Host's stub\n", symbolName);
|
||||
return reinterpret_cast<void*>(&hostSideDeletedFunctionStub);
|
||||
}
|
||||
|
||||
/// @param kind The event kind to name.
|
||||
/// @return A short, human-readable label for @p kind.
|
||||
const char* eventKindName(HotReloadSymbolEvent kind)
|
||||
{
|
||||
switch (kind)
|
||||
@@ -64,6 +73,8 @@ namespace
|
||||
return "?";
|
||||
}
|
||||
|
||||
/// Logs every symbol event except REMOVED, which onDeletedSymbol() already logs.
|
||||
/// @param event Describes one symbol's outcome.
|
||||
void onSymbolEvent(const HotReloadSymbolEventInfo* event, void* /*userData*/)
|
||||
{
|
||||
// REMOVED is already reported, with more context, by
|
||||
@@ -85,6 +96,10 @@ namespace
|
||||
|
||||
}
|
||||
|
||||
/// Runs the scripted demo described in this file's @ref Main.cpp "file comment".
|
||||
/// @param argc Argument count.
|
||||
/// @param argv `argv[1]`/`argv[2]`, if present, override the compiled-in plugin paths.
|
||||
/// @return 0 on success, 1 if the context or either load/reload step failed.
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::string pluginV1Path = argc > 1 ? argv[1] : PLUGIN_V1_PATH;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* CppWidget V1 -- "before your edit". See PluginV2/src/CppWidget.cpp
|
||||
/**
|
||||
* @file CppWidget.cpp
|
||||
* @brief CppWidget V1 -- "before your edit". See PluginV2/src/CppWidget.cpp
|
||||
* for "after": same three exports change behavior together, exactly
|
||||
* like PluginV1/src/Plugin.cpp's plain-C functions do.
|
||||
* like PluginV1/src/Plugin.cpp's plain-C functions do. Every member
|
||||
* defined here is documented in ../Shared/CppWidgetApi.h.
|
||||
*/
|
||||
#include "CppWidgetApi.h"
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/* PluginV1 -- "before your edit". See PluginV2/src/Plugin.cpp for
|
||||
/**
|
||||
* @file Plugin.cpp
|
||||
* @brief PluginV1 -- "before your edit". See PluginV2/src/Plugin.cpp for
|
||||
* "after"; the two deliberately export the identical set of names
|
||||
* (minus plugin_legacy_feature, removed in V2) so HotReloadContext can
|
||||
* line them up. See ../Shared/PluginApi.h for what each export is
|
||||
* there to demonstrate.
|
||||
* there to demonstrate (and for the documentation of every function
|
||||
* defined here).
|
||||
*/
|
||||
#include "PluginApi.h"
|
||||
|
||||
@@ -22,9 +25,8 @@ int plugin_legacy_feature(int input)
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
int plugin_tiny_function(int input)
|
||||
void plugin_tiny_function(void)
|
||||
{
|
||||
return input + 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/* CppWidget V2 -- "after your edit". See PluginV1/src/CppWidget.cpp
|
||||
* for "before".
|
||||
/**
|
||||
* @file CppWidget.cpp
|
||||
* @brief CppWidget V2 -- "after your edit". See PluginV1/src/CppWidget.cpp
|
||||
* for "before". Every member defined here is documented in
|
||||
* ../Shared/CppWidgetApi.h.
|
||||
*/
|
||||
#include "CppWidgetApi.h"
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
/* PluginV2 -- "after your edit". See PluginV1/src/Plugin.cpp for
|
||||
/**
|
||||
* @file Plugin.cpp
|
||||
* @brief PluginV2 -- "after your edit". See PluginV1/src/Plugin.cpp for
|
||||
* "before". plugin_tick's behavior changed, plugin_legacy_feature was
|
||||
* deleted, and plugin_new_feature was added -- exactly the three kinds
|
||||
* of change hot_reload.h's HotReloadSymbolEvent enum distinguishes.
|
||||
* Every function defined here is documented in ../Shared/PluginApi.h.
|
||||
*/
|
||||
#include "PluginApi.h"
|
||||
|
||||
@@ -24,9 +27,8 @@ int plugin_new_feature(int input)
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
int plugin_tiny_function(int input)
|
||||
void plugin_tiny_function(void)
|
||||
{
|
||||
return input + 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,47 +1,88 @@
|
||||
/* CppWidgetApi.h -- a C++ (not extern "C") surface living alongside
|
||||
/**
|
||||
* @file CppWidgetApi.h
|
||||
* @brief A C++ (not extern "C") surface living alongside
|
||||
* PluginApi.h's plain C one, in the SAME plugin build. Every member
|
||||
* function below keeps its real, compiler-mangled name; nothing here
|
||||
* needs special handling by source/HotReload, which classifies
|
||||
* symbols by their type and section, never by what their name looks
|
||||
* like -- see hot_reload.h's "REQUIREMENTS ON A HOT-RELOADABLE
|
||||
* MODULE" and README.md's "Hot reloading C++ symbols" for the full
|
||||
* picture.
|
||||
* like -- see the "Requirements on a hot-reloadable module" section of
|
||||
* hot_reload.h and the "Hot reloading C++ symbols" section of
|
||||
* source/HotReload/README.md for the full picture.
|
||||
*
|
||||
* The two methods below are reached two different ways from
|
||||
* Host/Main.cpp, deliberately: see that file's comments for why.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief A small C++ class whose members are defined in the plugin, not the host.
|
||||
*
|
||||
* Each member below keeps its real, compiler-mangled name and is reached a different way from
|
||||
* Host/Main.cpp -- through the vtable, through a manually resolved mangled name, and (for the
|
||||
* operator) not at all -- to show that all of them patch like any plain C function.
|
||||
*/
|
||||
class CppWidget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Virtual, so that destroying a widget through a base pointer works across the
|
||||
* boundary.
|
||||
*/
|
||||
virtual ~CppWidget() = default;
|
||||
|
||||
/* Virtual: Host/Main.cpp calls this through ordinary C++
|
||||
/**
|
||||
* @brief Scales its input, through a virtual call.
|
||||
*
|
||||
* Virtual: Host/Main.cpp calls this through ordinary C++
|
||||
* polymorphic dispatch (widget->virtualTick(x)) and needs no
|
||||
* manual symbol lookup at all -- the call compiles down to an
|
||||
* indirect call through the object's vtable slot, not a direct
|
||||
* reference to this specific symbol, so it needs no link-time
|
||||
* dependency on whichever plugin build actually defines it. */
|
||||
* dependency on whichever plugin build actually defines it.
|
||||
*
|
||||
* @param input The value to scale.
|
||||
* @return V1: `input * 2`. V2: `input * 3`.
|
||||
*/
|
||||
virtual int virtualTick(int input);
|
||||
|
||||
/* An ordinary out-of-line method. Calling this as widget->tick(x)
|
||||
/**
|
||||
* @brief Scales its input, through an ordinary out-of-line method.
|
||||
*
|
||||
* Calling this as widget->tick(x)
|
||||
* would compile to a direct call to CppWidget::tick's own mangled
|
||||
* symbol, which would need the Host to link against a plugin
|
||||
* build at build time -- exactly what hot-reloading a plugin
|
||||
* means avoiding. So Host/Main.cpp instead resolves it manually
|
||||
* by its mangled name via hot_reload_get_symbol(), the same way
|
||||
* it resolves PluginApi.h's plain C functions. */
|
||||
* it resolves PluginApi.h's plain C functions.
|
||||
*
|
||||
* @param input The value to scale.
|
||||
* @return V1: `input * 2`. V2: `input * 3`.
|
||||
*/
|
||||
int tick(int input);
|
||||
|
||||
/* An overloaded operator: mechanically just a function with an
|
||||
/**
|
||||
* @brief An overloaded operator.
|
||||
*
|
||||
* Mechanically just a function with an
|
||||
* unusual mangled name, reached the same manual way as tick()
|
||||
* above. */
|
||||
* above.
|
||||
*
|
||||
* @param other Ignored; the operator only prints which build it is.
|
||||
* @return A new, empty widget.
|
||||
*/
|
||||
CppWidget operator+(const CppWidget& other) const;
|
||||
};
|
||||
|
||||
/* The one entry point Host/Main.cpp looks up by a plain, human-typed
|
||||
/**
|
||||
* @brief Creates a widget.
|
||||
*
|
||||
* The one entry point Host/Main.cpp looks up by a plain, human-typed
|
||||
* name -- extern "C" here for exactly the reason hot_reload.h
|
||||
* recommends it: something to hand a human-readable name to, without
|
||||
* needing to know or hardcode a mangled string. */
|
||||
* needing to know or hardcode a mangled string.
|
||||
*
|
||||
* @return A new widget. The caller owns it and must `delete` it (safe across the plugin
|
||||
* boundary here because host and plugin share one C++ runtime and allocator).
|
||||
*/
|
||||
extern "C" CppWidget* make_cpp_widget();
|
||||
@@ -1,4 +1,6 @@
|
||||
/* PluginApi.h -- the contract PluginV1 and PluginV2 both implement,
|
||||
/**
|
||||
* @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.
|
||||
@@ -15,31 +17,64 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Changes behavior between V1 and V2 -- watch this one get
|
||||
* HOT_RELOAD_SYMBOL_PATCHED. */
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/* 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. */
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/* Only in V2 -- watch this one get HOT_RELOAD_SYMBOL_ADDED, then
|
||||
* get called for the first time right after. */
|
||||
/**
|
||||
* @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__)
|
||||
/* Deliberately too small to safely hold a jump on any of this
|
||||
* library's backends (its patchable-function-entry padding is
|
||||
* overridden down to zero) -- watch this one get
|
||||
* HOT_RELOAD_SYMBOL_SKIPPED_TOO_SMALL rather than corrupt whatever
|
||||
* comes after it. MSVC has no equivalent per-function override
|
||||
* this demo could apply, so this one only exists on GCC/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.
|
||||
*/
|
||||
__attribute__((patchable_function_entry(0))) int plugin_tiny_function(int input);
|
||||
__attribute__((patchable_function_entry(0))) void plugin_tiny_function(void);
|
||||
#endif
|
||||
|
||||
/* An exported VARIABLE, not a function, sitting right next to
|
||||
/**
|
||||
* @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
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* Main.cpp -- a persistent process, unlike source/examples/
|
||||
/**
|
||||
* @file Main.cpp
|
||||
* @brief Interactive demo: a persistent process, unlike source/examples/
|
||||
* HotReloadDemo/Host (which runs a fixed script and exits). This one
|
||||
* loads Plugin/src/LivePlugin.cpp's compiled output once, then polls
|
||||
* that file's last-write time every second: whenever it changes (i.e.
|
||||
@@ -18,13 +20,18 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
/// Cleared by handleSigint(); the main loop polls this to exit cleanly on Ctrl+C.
|
||||
std::atomic<bool> g_running{true};
|
||||
|
||||
/// SIGINT handler: requests a clean shutdown instead of the default terminate.
|
||||
/// The signal number itself is unused (this is only ever registered for SIGINT).
|
||||
void handleSigint(int /*signal*/)
|
||||
{
|
||||
g_running = false;
|
||||
}
|
||||
|
||||
/// @param kind The event kind to name.
|
||||
/// @return A short, human-readable label for @p kind.
|
||||
const char* eventKindName(HotReloadSymbolEvent kind)
|
||||
{
|
||||
switch (kind)
|
||||
@@ -45,6 +52,8 @@ namespace
|
||||
return "?";
|
||||
}
|
||||
|
||||
/// Logs every symbol event except REMOVED, which onDeletedSymbol() already logs.
|
||||
/// @param event Describes one symbol's outcome.
|
||||
void onSymbolEvent(const HotReloadSymbolEventInfo* event, void* /*userData*/)
|
||||
{
|
||||
if (event->kind == HOT_RELOAD_SYMBOL_REMOVED)
|
||||
@@ -54,6 +63,9 @@ namespace
|
||||
std::printf(" [reload] %-24s %s\n", eventKindName(event->kind), event->name);
|
||||
}
|
||||
|
||||
/// Stands in for @c live_tick once a rebuild removes it.
|
||||
/// @param frame The current frame number.
|
||||
/// @return Always -1.
|
||||
int fallbackForDeletedTick(int frame)
|
||||
{
|
||||
std::printf(
|
||||
@@ -63,6 +75,8 @@ namespace
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// @param symbolName The removed function's real (possibly mangled) name.
|
||||
/// @return The host fallback to redirect old callers to.
|
||||
void* onDeletedSymbol(const char* symbolName, void* /*userData*/)
|
||||
{
|
||||
std::printf(" [reload] REMOVED %s -> falling back to the Host's stub\n", symbolName);
|
||||
@@ -71,6 +85,11 @@ namespace
|
||||
|
||||
}
|
||||
|
||||
/// Runs the interactive demo described in this file's @ref Main.cpp "file comment".
|
||||
/// @param argc Argument count.
|
||||
/// @param argv `argv[1]`, if present, overrides the compiled-in plugin path.
|
||||
/// @return 0 on clean shutdown (Ctrl+C), 1 if the context could not be created or the initial
|
||||
/// load failed.
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Force line buffering regardless of whether stdout is a real
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* LivePlugin.cpp -- THE FILE TO EDIT.
|
||||
/**
|
||||
* @file LivePlugin.cpp
|
||||
* @brief THE FILE TO EDIT.
|
||||
*
|
||||
* With source/examples/HotReloadLiveDemo/Host running in one
|
||||
* terminal, rebuild just this file from another with:
|
||||
@@ -42,13 +44,19 @@
|
||||
#include "hot_reload/hot_reload.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* @brief The one function this demo starts with; edit this file and rebuild to see it change live.
|
||||
*
|
||||
* See this file's own comment for what to try.
|
||||
*
|
||||
* @param frame The Host's running frame counter, incremented once per second.
|
||||
* @return @p frame times the multiplier below.
|
||||
*/
|
||||
extern "C" HOT_RELOAD_EXPORT int live_tick(int frame)
|
||||
{
|
||||
int multiplier = 2; // <- try changing this
|
||||
int value = frame * multiplier;
|
||||
std::printf(" live_tick (%d) -> %d\n", frame, value);
|
||||
std::cout.flush();
|
||||
std::printf(" live_tick(%d) -> %d\n", frame, value);
|
||||
return value;
|
||||
}
|
||||
Reference in new issue
Block a user