如何创建宏避免重复定义基础相同的Class?以RegisterScript为例
Absolutely! Macros are a perfect fit for cutting down on repetitive class code—especially when you’re dealing with script registration workflows where every class follows the exact same structure. Let’s walk through how to build this, using a typical RegisterScript scenario as an example.
Let’s assume every class you need to define has:
- A default constructor/destructor
- A static
RegisterScript()method that binds the class and its methods to your script engine - A set of common methods with identical signatures (and possibly default implementations)
Here’s a macro that generates this structure automatically, just by passing the class name:
#define DEFINE_SCRIPT_BOUND_CLASS(CLASS_NAME) \ class CLASS_NAME { \ public: \ CLASS_NAME() = default; \ ~CLASS_NAME() = default; \ \ static void RegisterScript() { \ // Replace this with your actual script registration logic \ ScriptEngine::RegisterClass<CLASS_NAME>(); \ ScriptEngine:: RegisterMethod<CLASS_NAME, &CLASS_NAME::OnUpdate>("OnUpdate"); \ ScriptEngine:: RegisterMethod<CLASS_NAME, &CLASS_NAME::GetName>("GetName"); \ } \ \ void OnUpdate(float deltaTime) { \ // Default implementation (can be overridden if needed) \ } \ \ std::string GetName() const { \ return #CLASS_NAME; // Uses stringification to return the class name \ } \ };
How to Use It
Generating a new class is as simple as calling the macro with your desired class name:
DEFINE_SCRIPT_BOUND_CLASS(PlayerCharacter) DEFINE_SCRIPT_BOUND_CLASS(EnemyAI) DEFINE_SCRIPT_BOUND_CLASS(InteractiveObject)
Each of these will expand into a full class with all the boilerplate you need, no repeated code required.
If some classes need to override default methods or add extra members, you can adjust the macro to support virtual functions:
#define DEFINE_SCRIPT_BOUND_CLASS(CLASS_NAME) \ class CLASS_NAME { \ public: \ CLASS_NAME() = default; \ virtual ~CLASS_NAME() = default; \ \ static void RegisterScript() { \ ScriptEngine::RegisterClass<CLASS_NAME>(); \ ScriptEngine:: RegisterMethod<CLASS_NAME, &CLASS_NAME::OnUpdate>("OnUpdate"); \ ScriptEngine:: RegisterMethod<CLASS_NAME, &CLASS_NAME::GetName>("GetName"); \ } \ \ virtual void OnUpdate(float deltaTime) { \ // Default implementation \ } \ \ virtual std::string GetName() const { \ return #CLASS_NAME; \ } \ };
Then you can extend the class normally if you need custom behavior:
class PlayerCharacter : public PlayerCharacterBase { public: void OnUpdate(float deltaTime) override { // Custom player update logic PlayerCharacterBase::OnUpdate(deltaTime); // Call default if needed } };
If macros feel too "hacky" for your taste, you can use the Curiously Recurring Template Pattern (CRTP) to achieve similar results without preprocessor magic:
template<typename Derived> class ScriptBoundBase { public: static void RegisterScript() { ScriptEngine::RegisterClass<Derived>(); ScriptEngine:: RegisterMethod<Derived, &Derived::OnUpdate>("OnUpdate"); ScriptEngine:: RegisterMethod<Derived, &Derived::GetName>("GetName"); } virtual void OnUpdate(float deltaTime) = default; virtual std::string GetName() const { return typeid(Derived).name(); // Less clean than macro stringification, but works } }; // Usage class PlayerCharacter : public ScriptBoundBase<PlayerCharacter> { // Override methods as needed };
That said, macros are often more straightforward for this exact use case—they let you generate full class definitions with unique names without needing to inherit from a base template.
- Always wrap your macro in
#undefif you’re redefining it later to avoid conflicts:#undef DEFINE_SCRIPT_BOUND_CLASS - When debugging, remember that macros expand inline—you can check your compiler’s preprocessed output to verify the generated code
- Keep the macro focused on the repetitive boilerplate; leave custom logic to class extensions or overrides
内容的提问来源于stack exchange,提问作者user6200208

