// ----------------------------------------------------------------------------- // file="IStaticModule" // Project: StgSharp // Copyright (c) Nitload. // SPDX-License-Identifier: MIT // ----------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; namespace StgSharp { /// /// Represents a statically activated module that can be registered and initialized as part of /// the application startup sequence, and gracefully torn down on shutdown. /// public interface IStaticModule { /// /// Gets the display name of this module, used for logging and diagnostics. /// string ModuleName { get; } /// /// Performs all initialization work for this module using the provided profile. /// /// /// The configuration profile supplied at registration time. Use if no configuration is required. /// void InitializeModule( IModuleInitializeProfile profile ); /// /// Releases all resources acquired during . Implementations should /// be idempotent: calling this method more than once must not throw. /// void UninitializeModule(); } /// /// Marker interface for module initialization configuration. Implementations are plain data /// carriers; no static factory is required. /// public interface IModuleInitializeProfile { /// /// A shared, allocation-free empty profile for modules that require no configuration. /// static readonly IModuleInitializeProfile Empty = EmptyModuleInitializeProfile.Instance; private sealed class EmptyModuleInitializeProfile : IModuleInitializeProfile { public static readonly EmptyModuleInitializeProfile Instance = new(); private EmptyModuleInitializeProfile() { } } } /// /// Pairs an with its , /// ensuring the two are never stored or iterated independently. /// /// /// Using a avoids the boxing overhead that would occur if /// the pair were stored as two parallel lists of reference types. /// public readonly struct ModuleDescriptor { /// /// Initializes a new using as the profile. /// public ModuleDescriptor( IStaticModule module ) : this(module, IModuleInitializeProfile.Empty) { } /// /// Initializes a new with an explicit profile. /// public ModuleDescriptor( IStaticModule module, IModuleInitializeProfile profile ) { Module = module; Profile = profile; } /// /// Gets the configuration profile associated with the module. /// public IModuleInitializeProfile Profile { get; } /// /// Gets the module to be initialized. /// public IStaticModule Module { get; } /// /// Calls with the stored profile. /// public void Initialize() { Module.InitializeModule(Profile); } /// /// Calls on the stored module. /// public void Uninitialize() { Module.UninitializeModule(); } } /// /// An ordered collection of modules to be initialized during application startup and /// uninitialized on shutdown. Supports three registration strategies so that the new() /// generic constraint is never a hard requirement. /// public sealed class ModuleToInitializeCollection : IEnumerable, IStaticModule { private readonly List _descriptors = []; internal ModuleToInitializeCollection( World.Initializer i ) { _descriptors.Add(new ModuleDescriptor(i)); } // ── IEnumerable ─────────────────────────────────────────── /// public IEnumerator GetEnumerator() { foreach (ModuleDescriptor d in _descriptors) { yield return d.Module; } } // ── Initialization / Uninitialization ──────────────────────────────────── /// /// Initializes all registered modules in the order they were registered. Each module /// receives the profile that was supplied at registration time. /// public void InitializeModule() { for (int i = 0; i < _descriptors.Count; i++) { _descriptors[i].Initialize(); } } /// /// Uninitializes all registered modules in reverse registration order, mirroring /// the typical LIFO teardown convention (e.g., GLFW must be unloaded after all subsystems /// that depend on it). /// public void UninitializeModule() { for (int i = _descriptors.Count - 1; i >= 0; i--) { _descriptors[i].Uninitialize(); } } // ── Registration API ───────────────────────────────────────────────────── /// /// Registers a module of type using its default constructor. /// This is the most concise overload for simple modules that expose a public /// parameterless constructor. /// /// /// A concrete module type that implements and exposes a /// public parameterless constructor. /// /// /// Optional initialization profile. Defaults to when . /// public ModuleToInitializeCollection UseModule( IModuleInitializeProfile? profile = null ) where T : IStaticModule, new() { return UseModule(static() => new T(), profile); } /// /// Registers a module produced by a factory delegate. This overload imposes no generic /// constraints and supports any construction strategy, including dependency injection and /// parameterized constructors. /// /// /// A delegate that constructs and returns the module instance. The delegate is invoked /// once, immediately at registration time. If is or returns , the call is silently ignored /// and the collection is returned unchanged. /// /// /// Optional initialization profile. Defaults to when . /// public ModuleToInitializeCollection UseModule( Func factory, IModuleInitializeProfile? profile = null ) { if (factory is null) { return this; } IStaticModule? mod = factory(); if (mod is null) { return this; } _descriptors.Add(new ModuleDescriptor( mod, profile ?? IModuleInitializeProfile.Empty)); return this; } /// /// Registers a pre-existing module instance. Use this overload when the module is /// provided externally, such as from a DI container, an object pool, or a singleton. /// /// /// The module instance to register. /// /// /// Optional initialization profile. Defaults to when . /// public ModuleToInitializeCollection UseModule( IStaticModule module, IModuleInitializeProfile? profile = null ) { _descriptors.Add(new ModuleDescriptor( module, profile ?? IModuleInitializeProfile.Empty)); return this; } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } // ── IStaticModule explicit implementation ──────────────────────────────── /// /// Explicit implementation. Delegates to , ignoring the supplied profile because each contained module uses /// its own registered profile. /// void IStaticModule.InitializeModule( IModuleInitializeProfile profile ) { InitializeModule(); } /// string IStaticModule.ModuleName => "Module Initializer"; } }