From e24186fc1917c5e8b91924af0c3c7c55816ff5d6 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Mon, 25 Jan 2021 22:01:00 +0100 Subject: Introducing MIC --- README.md | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 README.md (limited to 'README.md') diff --git a/README.md b/README.md new file mode 100644 index 0000000..56d8b9d --- /dev/null +++ b/README.md @@ -0,0 +1,118 @@ +# Micro Interactive C Framework (MIC) + +MIC is a tiny (<300 cloc) framework to boostrap interactive C projects. +Application code can be dynamically reloaded upon compilation, allowing C to be +used as a pseudo-scripting language. This property can be very useful when +developing, but might not be always desirable for a final release, for this +reason, static compilation can be selected as a make target when needed. The +main limitation of hot code reload is that global state can't be stored in the +main app code, but the framework is designed so the state is passed around when +needed and is always accessible for the relevant functions. + +This framework can be easily extended to work with multiple platforms. However, +hot code reload will only be available for platforms that support dynamic +linking. For the moment, only POSIX compliant platforms are supported but +Windows is a target for the near future. + +Remember that MIC is only a base, you can (and should) modify anything in here +to adapt it to your particular project. + +## Example usage + +Compile and execute the program: + +``` +make && ./build/app +``` + +While the application is running, make changes to the `src/app.c`. For example, +change the `app_step` function from: + +``` +static inline bool +app_step(AppState *state, PlatformAPI platform) { + (void)state; // Unused parameter. + platform.log("STEP"); + platform.sleep(100000); + return true; +} +``` + +To: + +``` +static inline bool +app_step(AppState *state, PlatformAPI platform) { + (void)state; // Unused parameter. + platform.log("Hello world!"); + platform.sleep(100000); + return true; +} +``` + +In another terminal (or using your editor of choice), recompile the program: + +``` +make +``` + +The changes should take effect in the running application. + +## Implementation details + +This framework offers two APIs for managing system resources and executing +application logic. The `PlatformAPI` is described in `src/platform.h` and it's +tasked with manage the resources of the platform and/or operating system. For +example, opening/reading files, allocating memory, logging, etc. To support +a new platform it is sufficient to implement the functions described on the API +and make sure to select such platform at compile time. Additionally, the +following internal functions must be implemented for static and dynamic linking: + +``` +// Load the dynamic library and initialize application. Returns the success or +// failure of this operation. +static bool _app_init(AppAPI *api, AppState *state, PlatformAPI platform); + +// This function reloads the application code from the dynamic library, +// returning `true` if the AppAPI is ready to be used. +static bool _app_reload(); + +// Cleanup resources before exit. +static void _app_destroy(AppAPI *api, AppState *state, PlatformAPI platform); +``` + +The `AppAPI`, described in `src/app.h` and handles app initialization and +destruction, it contains the behaviour in the event of hot code reloading as +well as the main step function. The `init` function typically will be tasked +with initial allocation of resources. The duty of `reload` and `unload` is +application dependent, for example, we might wish to re-compile the shader +programs or reload geometry/sprites when we make modifications to the main +application code. The `step` function is called in every iteration of the loop, +and is where the bulk of the logic will likely be. + +The application state is stored in a unique `AppState` structure. MIC has been +architected so that the state and platform functions are available in the +previously mentioned `AppAPI` functions. `AppState` can contain whatever state +is needed and will be persistent when re-compiling. The only limitation is that +if the `AppState` structure changes, the application must be restarted, as the +stored state will become invalid. This can be circumvented by pre-allocating +some memory that can then be partitioned with a pool allocator to suit the +application needs. This design is also conducive for saving state data by simply +serializing the `AppState` structure. + +## Credits + +I initially discovered the hot code reloading trick when watching [Casey +Muratori's][casey] [Handmade Hero][handmade] series. It blew my mind! However, +the code he presented was focused solely on Windows and I never end up trying it +myself. Some years later I tried to find a way of making this work in Mac/Linux +when I came across [Chris Wellons'][skeeto] take on this problem with a clever +function pointer API in his [interactive c demo][interac-c-demo] page. This +framework uses a lot of the ideas he presents there, as you can clearly see if +you look at [his code][interac-c-demo]. + +[casey]: https://caseymuratori.com +[handmade]: https://handmadehero.org +[skeeto]: https://nullprogram.com +[interac-c-demo]: https://nullprogram.com/blog/2014/12/23/ +[interac-c-demo-github]: https://github.com/skeeto/interactive-c-demo -- cgit v1.2.1