Created
January 19, 2026 12:51
-
-
Save unrays/24862aa7ff2a2dc327e675c3b846ab5a to your computer and use it in GitHub Desktop.
Compile-time event hooks using CRTP in modern C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Copyright (c) December 2025 Félix-Olivier Dumas. All rights reserved. | |
| // Licensed under the terms described in the LICENSE file | |
| template<typename Hooked> | |
| struct IEventHookable { | |
| protected: | |
| IEventHookable() { | |
| if constexpr (requires(Hooked h) { h.onCreated(); }) | |
| static_cast<Hooked*>(this)->onCreated(); | |
| else onCreatedDefault(); | |
| } | |
| ~IEventHookable() { | |
| if constexpr (requires(Hooked h) { h.onDestroyed(); }) | |
| static_cast<Hooked*>(this)->onDestroyed(); | |
| else onDestroyedDefault(); | |
| } | |
| protected: | |
| void invokePreUpdate() { | |
| if constexpr (requires(Hooked h) { h.onPreUpdate(); }) | |
| static_cast<Hooked*>(this)->onPreUpdate(); | |
| else onPreUpdateDefault(); | |
| } | |
| void invokeUpdate() { | |
| invokePreUpdate(); | |
| if constexpr (requires(Hooked h) { h.onUpdate(); }) | |
| static_cast<Hooked*>(this)->onUpdate(); | |
| else onUpdateDefault(); | |
| invokePostUpdate(); | |
| } | |
| void invokePostUpdate() { | |
| if constexpr (requires(Hooked h) { h.onPostUpdate(); }) | |
| static_cast<Hooked*>(this)->onPostUpdate(); | |
| else onPostUpdateDefault(); | |
| } | |
| protected: | |
| void onCreatedDefault() { std::cout << "No 'onCreated' using default.\n"; } | |
| void onDestroyedDefault() { std::cout << "No 'onDestroyed' using default.\n"; } | |
| void onUpdateDefault() { std::cout << "No 'onUpdate' using default.\n"; } | |
| void onPreUpdateDefault() { std::cout << "No 'onPreUpdate' using default.\n"; } | |
| void onPostUpdateDefault() { std::cout << "No 'onPostUpdate' using default.\n"; } | |
| }; | |
| template<typename Derived> | |
| struct IUpdatable { | |
| void update() | |
| requires std::is_base_of_v<IEventHookable<Derived>, Derived> | |
| { static_cast<Derived*>(this)->invokeUpdate(); } | |
| }; | |
| struct System : public IEventHookable<System>, public IUpdatable<System> { | |
| public: friend IEventHookable<System>; friend IUpdatable<System>; | |
| private: | |
| void onCreated() { | |
| std::cout << "Creating new system...\n"; | |
| } | |
| void onUpdate() { | |
| std::cout << "Updating System...\n"; | |
| } | |
| void onDestroyed() { | |
| std::cout << "Destroying system...\n"; | |
| } | |
| }; | |
| int main() { | |
| System sys; | |
| sys.update(); | |
| //only exposes .update() | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment