Common System 0.2.0
Common interfaces and patterns for system integration
Loading...
Searching...
No Matches
Frequently Asked Questions

Result<T> Questions

When should I use Result<T> instead of exceptions?

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.

How do I chain Result<T> operations without nesting?

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.

Why doesn't .value() just return a default on error?

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.

Dependency Injection Questions

How does the service container lifecycle work?

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.

Can I use common_system without the rest of the ecosystem?

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.

Build and Integration Questions

What C++ version is required?

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.

How do I integrate with existing exception-based code?

Wrap the exception boundary with a try/catch that converts to Result:

Result<int> safe_parse(const std::string& s) {
try {
return make_ok(std::stoi(s));
} catch (const std::exception& e) {
return make_error<int>(error_code::parse_failed, e.what());
}
}

Do this once at the boundary — not in every function.

Threading Questions

Is the service container thread-safe?

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.

Does the event bus dispatch on a worker thread?

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.

Testing Questions

How do I mock a service for testing?

Create a test double that implements the interface and register it instead of the production implementation. See Tutorial: Dependency Injection for the pattern.

How do I test Result-returning functions?

Check both branches explicitly:

TEST(Parser, rejects_empty_input) {
auto r = parse("");
ASSERT_TRUE(r.is_error());
EXPECT_EQ(r.error().code, error_code::invalid_argument);
}

Performance Questions

Is Result<T> expensive compared to exceptions?

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.

Deployment Questions

Is common_system header-only?

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.