API Reference¶
Core Types¶
Arc¶
Arc<T> is an alias for std::shared_ptr<T>. Used to hold references to services.
MakeArc(args...)¶
Creates a Arc<T> instance. Factory function for service creation.
template <typename T, typename... TArgs>
requires(std::is_constructible_v<T, TArgs...>)
Arc<T> MakeArc(TArgs&&... args);
Lifetime¶
Enum specifying a service lifetime:
| Value | Description |
|---|---|
| Transient | New instance each request |
| Scoped | One instance per scope |
| Singleton | Single instance per application |
ServiceFactory¶
Function type for service factories:
ServiceCollection¶
Lifetime Registration Methods¶
Concrete Type Registration¶
ServiceCollection& AddSingleton<TService>();
ServiceCollection& AddScoped<TService>();
ServiceCollection& AddTransient<TService>();
Contract/Interface Registration¶
ServiceCollection& AddSingleton<TContract, TService>();
ServiceCollection& AddScoped<TContract, TService>();
ServiceCollection& AddTransient<TContract, TService>();
Factory Registration¶
ServiceCollection& AddSingleton<TService>(const ServiceFactory& factory);
ServiceCollection& AddScoped<TService>(const ServiceFactory& factory);
ServiceCollection& AddTransient<TService>(const ServiceFactory& factory);
Instance Registration¶
ServiceCollection& AddSingleton<TService>(Arc<TService> element);
ServiceCollection& AddSingleton<TContract, TService>(Arc<TService> element);
Keyed / Named Registration¶
Register multiple implementations of one contract distinguished by a string key. See Keyed Services.
ServiceCollection& AddKeyedSingleton<TContract, TService>(std::string key);
ServiceCollection& AddKeyedScoped<TContract, TService>(std::string key);
ServiceCollection& AddKeyedTransient<TContract, TService>(std::string key);
Utility Methods¶
ServiceProvider¶
Service Retrieval¶
template <typename TService>
Arc<TService> GetService();
template <typename TService>
std::optional<Arc<TService>> TryGetService();
template <typename TService>
std::vector<Arc<TService>> GetServices();
template <typename TService>
Arc<TService> GetKeyedService(std::string_view key);
template <typename TService>
std::optional<Arc<TService>> TryGetKeyedService(std::string_view key);
Validation and Diagnostics¶
Utility Methods¶
Late Registration¶
Register or remove services after CreateServiceProvider(). Overloads mirror ServiceCollection (AddSingleton / AddTransient / AddScoped with factory, instance, and contract variants). See Late Registration.
ServiceProvider& AddSingleton<TService>();
ServiceProvider& AddTransient<TService>();
ServiceProvider& AddScoped<TService>();
// ... contract, factory, and instance overloads
template <typename TService>
bool Remove();
Remove<T>() erases every registration for T and evicts that id from singleton, keyed-singleton, and live scoped caches.
ServiceScope¶
Constructor¶
ServiceScope(const Arc<ServiceDefinitionMap>& serviceDefinitionMap,
const Arc<ServicesCache>& singletonsCache,
const Arc<KeyedServicesCache>& keyedSingletonsCache,
const Arc<ScopeCacheRegistry>& scopeCacheRegistry,
const Arc<ServicesCache>& scopeCache);
Methods¶
ServiceId¶
using ServiceId = unsigned long;
ServiceId RegisterTypeName(std::string_view typeName);
template <typename T>
ServiceId GetServiceId();
GetServiceId<T>() caches a dense id from RegisterTypeName(refl::type_name<T>()). The same type name always maps to the same id in-process (safe across static libs / DSOs that share Skirnir).
Injection Wrappers¶
The container inspects each constructor parameter at resolution time and dispatches based on its type:
| Wrapper | Resolved by |
|---|---|
Arc<T> | GetService<T>() — first registration for T. |
std::vector<Arc<T>> | GetServices<T>() — every registration for T. |
std::optional<Arc<T>> | TryGetService<T>() — nullopt when T is not registered. |
Keyed<T, NTTP> | GetKeyedService<T>(NTTP) — see Keyed Services. |
Keyed<T, NTTP> currently only accepts a NTTP that points to a static character array (inline constexpr char key[] = "...";). Other NTTP types are accepted by the template but not resolved by the container.
std::optional<Keyed<T, "k">> is not special-cased; nested optional wrappers around Keyed will fail to resolve.
Logger¶
Log Levels¶
| Level | Description |
|---|---|
| Debug | Debug messages (default in debug) |
| Trace | Trace messages (default in release) |
| Information | General information messages |
| Warning | Warning messages |
| Error | Error messages |
| Fatal | Fatal errors (throws exception) |
| None | Disables all logging |
Logging Methods¶
Format-string API (existing):
template <typename... TArgs>
void LogDebug(std::format_string<TArgs...> fmt, TArgs&&... args);
template <typename... TArgs>
void LogTrace(std::format_string<TArgs...> fmt, TArgs&&... args);
template <typename... TArgs>
void LogInformation(std::format_string<TArgs...> fmt, TArgs&&... args);
template <typename... TArgs>
void LogWarning(std::format_string<TArgs...> fmt, TArgs&&... args);
template <typename... TArgs>
void LogError(std::format_string<TArgs...> fmt, TArgs&&... args);
template <typename... TArgs>
void LogFatal(std::format_string<TArgs...> fmt, TArgs&&... args);
template <typename... TArgs>
void Assert(bool assertion, std::format_string<TArgs...> fmt, TArgs&&... args);
LogRecord¶
A complete log entry, ready to be handed to a sink.
struct LogRecord
{
LogLevel level;
std::chrono::system_clock::time_point timestamp;
std::string_view category;
std::string message;
std::vector<std::string_view> scopes;
std::source_location location;
};
ILogSink¶
class ILogSink
{
public:
virtual ~ILogSink() = default;
virtual void Write(const LogRecord& record) = 0;
virtual void Flush() {}
};
Built-in Sinks¶
| Class | Constructor |
|---|---|
NullSink | NullSink() |
ConsoleSink | ConsoleSink(bool useColors = true) |
FileSink | FileSink(path, bool autoFlush = true) |
JsonSink | JsonSink(std::ostream&) or JsonSink(path) |
AsyncSink | AsyncSink(Arc<ILogSink> inner, size_t capacity) |
AsyncSink::DroppedCount() returns the number of records dropped due to a full queue.
LogScope¶
RAII handle returned by LoggerOptions::BeginScope.
LoggerOptions¶
Configuration class for logging behavior:
class LoggerOptions {
LogLevel logLevel; // Default: Debug (debug build), Trace (release)
// Sink management
LoggerOptions& AddSink(Arc<ILogSink> sink);
const std::vector<Arc<ILogSink>>& Sinks() const noexcept;
void ClearSinks();
// Dispatch (internal — called by Logger<T>)
void Dispatch(const LogRecord& record);
// Scopes
Arc<LogScope> BeginScope(std::string name);
// Configuration
void ConfigureFrom(Arc<ConfigurationOptions> config,
std::string_view path = "logging.logLevel.default");
template <typename T> LogLevel GetLogLevelFor();
};
Inject Arc<LoggerOptions> to customize logging.
Configuration¶
ConfigurationBuilder¶
ConfigurationBuilder& AddJsonFile(const std::filesystem::path& path);
ConfigurationBuilder& AddJsonString(std::string_view json);
ConfigurationBuilder& AddSource(Arc<IConfigurationSource> source);
ConfigurationBuilder& AddInMemory(
std::initializer_list<std::pair<std::string, std::string>> entries);
ConfigurationBuilder& AddEnvironmentVariables(std::string prefix = {});
Arc<ConfigurationOptions> Build();
AddEnvironmentVariables(prefix) registers an EnvironmentVariablesSource that reads from std::getenv. With a non-empty prefix only matching variables are loaded (the prefix is stripped from the resulting key), and double underscores (__) are translated to dots (.) so nested sections can be expressed (SKIRNIR_DB__HOST=db.local → { "db": { "host": "db.local" } }).
ConfigurationOptions¶
std::optional<std::string> GetValue(std::string_view key) const;
bool HasKey(std::string_view key) const;
bool GetBool(std::string_view key, bool defaultValue = false) const;
int64_t GetInt(std::string_view key, int64_t defaultValue = 0) const;
double GetDouble(std::string_view key, double defaultValue = 0.0) const;
std::string GetString(std::string_view key, std::string_view defaultValue = "") const;
std::vector<std::string> GetArray(std::string_view key) const;
Arc<ConfigurationOptions> GetSection(std::string_view key) const;
template <typename T>
T Bind(std::string_view section = "") const;
IConfigurationSource¶
Abstract base class for custom configuration sources. Override Load() to produce a simdjson::dom::element representing the root of your data.
JsonObjectReader¶
bool Contains(std::string_view key) const;
template <typename T> bool TryGet(std::string_view key, T& out) const;
Application¶
IApplication¶
Abstract base class for applications:
class IApplication {
public:
IApplication(const Arc<ServiceProvider>& rootServiceProvider);
virtual ~IApplication() = default;
Arc<ServiceProvider> GetRootServiceProvider() const;
virtual void Run() = 0;
};
The IApplication singleton is automatically registered when using ApplicationBuilder.
ApplicationBuilder¶
For registering applications with the container:
Methods¶
Arc<ServiceCollection> GetServiceCollection();
template <typename TExtension>
requires(std::is_base_of_v<IExtension, TExtension>)
ApplicationBuilder& WithExtension();
template <typename TExtension>
requires(std::is_base_of_v<IExtension, TExtension>)
ApplicationBuilder& WithExtension(
std::function<void(TExtension&)> configureExtensionFunc);
ApplicationBuilder& WithConfiguration(
std::function<void(ConfigurationBuilder&)> configureFunc);
template <typename TApplication>
requires(std::is_base_of_v<IApplication, TApplication>)
Arc<TApplication> Build();
WithExtension registers an extension, calling its Attach hook immediately and the ConfigureServices / UseServices hooks during Build(). Calling it twice for the same TExtension retrieves the existing instance and re-runs the configuration callback.
WithConfiguration exposes the underlying ConfigurationBuilder so configuration sources can be registered inline with services.
Build() registers the application type as a singleton, resolves the configuration, runs every extension's UseServices hook, and returns the resolved application instance.