|
Common System 0.2.0
Common interfaces and patterns for system integration
|
Use Result<T> when the failure is an expected part of the API contract: configuration parsing, network timeouts, missing files, invalid user input. Use exceptions for genuinely exceptional situations: out-of-memory, programmer errors like precondition violations, and corruption.
The boundary matters more than the choice: never leak exceptions across a module that promises Result<T>, and never return Result<T> from a function whose caller expects exceptions.
Use .and_then() / .or_else() — each step composes onto the previous one and short-circuits on the first error. See Result Tutorial for a worked example.
Because silently swallowing errors is how bugs hide for months. If you genuinely want a default, say so explicitly with .value_or(default). .value() on an error is a contract violation and should fail loudly.
Singletons live for the lifetime of the container. Transients are created on each resolve and owned by the caller. The container itself is process-global by default (service_container::instance()), but you can create local instances for isolated test scopes.
Yes. common_system is deliberately the foundation layer and has no dependencies on the other kcenon systems. You can pull it in for Result<T>, the service container, and the event bus without committing to the full stack.
C++20. Specifically, the code uses std::format, concepts, and three-way comparison, so the minimum compiler baseline is GCC 13, Clang 17, or MSVC 2022.
Wrap the exception boundary with a try/catch that converts to Result:
Do this once at the boundary — not in every function.
Yes for lookup and resolve. Registration is also thread-safe but intended to happen during application startup before any resolves — registering from multiple threads while others are resolving is supported but stylistically suspect.
No — handlers run synchronously on the publisher's thread. If you need async dispatch, wrap the handler in a lambda that posts work to thread_system's pool.
Create a test double that implements the interface and register it instead of the production implementation. See Tutorial: Dependency Injection for the pattern.
Check both branches explicitly:
In the happy path, Result<T> is usually cheaper: no stack unwinding, no RTTI lookup, and the compiler can often inline the check away. In the error path, Result is consistently cheap while exceptions are consistently expensive.
Most of it is. Some patterns (event bus, circuit breaker) have small .cpp files. The CMake target handles both cases transparently — just link kcenon::common_system.