Common System 0.2.0
Common interfaces and patterns for system integration
Loading...
Searching...
No Matches
kcenon::common::di::service_container Class Reference

Concrete implementation of IServiceContainer. More...

#include <service_container.h>

Inheritance diagram for kcenon::common::di::service_container:
Inheritance graph
Collaboration diagram for kcenon::common::di::service_container:
Collaboration graph

Classes

struct  service_entry
 Internal service registration entry. More...
 

Public Member Functions

 service_container ()=default
 Default constructor.
 
 ~service_container () override=default
 Destructor.
 
 service_container (const service_container &)=delete
 
service_containeroperator= (const service_container &)=delete
 
 service_container (service_container &&)=delete
 
service_containeroperator= (service_container &&)=delete
 
std::unique_ptr< IServiceScopecreate_scope () override
 Create a new service scope.
 
std::vector< service_descriptorregistered_services () const override
 Get list of all registered service descriptors.
 
VoidResult clear () override
 Clear all registrations.
 
void freeze ()
 Freeze the container to prevent further registrations.
 
bool is_frozen () const
 Check if the container is frozen.
 
- Public Member Functions inherited from kcenon::common::di::IServiceContainer
virtual ~IServiceContainer ()=default
 
template<concepts::ServiceInterface TInterface, concepts::ServiceImplementation< TInterface > TImpl>
VoidResult register_type (service_lifetime lifetime=service_lifetime::singleton)
 Register a service type with its implementation.
 
template<typename TInterface >
VoidResult register_instance (std::shared_ptr< TInterface > instance)
 Register a pre-existing instance as a singleton.
 
template<concepts::ServiceInterface TInterface, concepts::ServiceFactory< TInterface > TFactory>
VoidResult register_factory (TFactory &&factory, service_lifetime lifetime=service_lifetime::singleton)
 Register a factory function for creating service instances.
 
template<concepts::ServiceInterface TInterface, concepts::SimpleServiceFactory< TInterface > TFactory>
VoidResult register_simple_factory (TFactory &&factory, service_lifetime lifetime=service_lifetime::singleton)
 Register a simple factory function without container access.
 
template<typename TInterface >
Result< std::shared_ptr< TInterface > > resolve ()
 Resolve a service by its interface type.
 
template<typename TInterface >
std::shared_ptr< TInterface > resolve_or_null ()
 Try to resolve a service, returning nullptr if not found.
 
template<typename TInterface >
bool is_registered () const
 Check if a service type is registered.
 
template<typename TInterface >
VoidResult unregister ()
 Unregister a service type.
 
virtual ~IServiceContainer ()=default
 
template<typename Interface , typename Factory >
void register_factory (Factory &&factory)
 Register a service with a factory function.
 
template<typename Interface >
void register_singleton (std::shared_ptr< Interface > instance)
 Register a singleton instance.
 
template<typename Interface >
std::shared_ptr< Interface > resolve ()
 Resolve a service by interface type.
 
template<typename Interface >
bool has () const
 Check if a service is registered.
 

Static Public Member Functions

static service_containerglobal ()
 Get the global service container instance.
 

Protected Member Functions

VoidResult register_factory_internal (std::type_index interface_type, const std::string &type_name, std::function< std::shared_ptr< void >(IServiceContainer &)> factory, service_lifetime lifetime) override
 Internal factory registration (type-erased).
 
VoidResult register_instance_internal (std::type_index interface_type, const std::string &type_name, std::shared_ptr< void > instance) override
 Internal instance registration (type-erased).
 
Result< std::shared_ptr< void > > resolve_internal (std::type_index interface_type) override
 Internal service resolution (type-erased).
 
bool is_registered_internal (std::type_index interface_type) const override
 Internal registration check (type-erased).
 
VoidResult unregister_internal (std::type_index interface_type) override
 Internal unregistration (type-erased).
 
- Protected Member Functions inherited from kcenon::common::di::IServiceContainer
virtual void register_impl (std::type_index type, std::any factory)=0
 
virtual void register_singleton_impl (std::type_index type, std::any instance)=0
 
virtual std::any resolve_impl (std::type_index type)=0
 
virtual bool has_impl (std::type_index type) const =0
 

Private Member Functions

Result< std::shared_ptr< void > > resolve_with_detection (std::type_index interface_type, std::unordered_map< std::type_index, std::shared_ptr< void > > *scoped_instances=nullptr)
 Resolve a service with circular dependency detection.
 
bool is_resolving (std::type_index interface_type) const
 Check if currently resolving this type (circular dependency check).
 
void push_resolution (std::type_index interface_type)
 Push type onto resolution stack.
 
void pop_resolution (std::type_index interface_type)
 Pop type from resolution stack.
 
std::string get_resolution_stack_string () const
 Get current resolution stack as string for error messages.
 
VoidResult check_frozen_for_registration (const std::string &type_name, interfaces::registry_action action, const std::string &error_message) const
 Check if container is frozen and log audit event if so.
 
VoidResult check_already_registered (std::type_index interface_type, const std::string &type_name) const
 Check if a service is already registered and log audit event if so.
 
Result< std::shared_ptr< void > > invoke_factory_safe (const std::function< std::shared_ptr< void >(IServiceContainer &)> &factory)
 Safely invoke a factory function with exception handling.
 

Private Attributes

std::shared_mutex mutex_
 
std::unordered_map< std::type_index, service_entryservices_
 
std::atomic< bool > frozen_ {false}
 

Static Private Attributes

static thread_local std::unordered_set< std::type_index > resolution_stack_
 
static thread_local std::vector< std::type_index > resolution_order_
 

Friends

class service_scope
 

Detailed Description

Concrete implementation of IServiceContainer.

Provides a thread-safe dependency injection container with support for:

  • Singleton, transient, and scoped service lifetimes
  • Factory-based lazy instantiation
  • Circular dependency detection
  • Scoped containers for request-level isolation

Usage Example:

auto& container = service_container::global();
// Register a singleton logger
container.register_factory<ILogger>(
[](IServiceContainer&) { return std::make_shared<ConsoleLogger>(); },
);
// Register a transient service
container.register_type<IWorker, WorkerImpl>(service_lifetime::transient);
// Resolve services
auto logger = container.resolve<ILogger>().value();
Abstract interface for dependency injection containers.
Definition di.cppm:39
static service_container & global()
Get the global service container instance.
@ singleton
Single instance shared globally.
@ transient
New instance created for each request.

Definition at line 66 of file service_container.h.

Constructor & Destructor Documentation

◆ service_container() [1/3]

kcenon::common::di::service_container::service_container ( )
default

Default constructor.

◆ ~service_container()

kcenon::common::di::service_container::~service_container ( )
overridedefault

Destructor.

◆ service_container() [2/3]

kcenon::common::di::service_container::service_container ( const service_container & )
delete

◆ service_container() [3/3]

kcenon::common::di::service_container::service_container ( service_container && )
delete

Member Function Documentation

◆ check_already_registered()

VoidResult kcenon::common::di::service_container::check_already_registered ( std::type_index interface_type,
const std::string & type_name ) const
inlineprivate

Check if a service is already registered and log audit event if so.

Note
Caller must hold the mutex lock before calling this method.
Parameters
interface_typeThe type to check
type_nameThe type name for audit logging
Returns
VoidResult - error if already registered, ok otherwise

Definition at line 435 of file service_container.h.

437 {
438
439 if (services_.find(interface_type) != services_.end()) {
440 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
443 "Service already registered"));
446 "Service already registered: " + type_name,
447 "di::service_container"
448 );
449 }
450
451 return VoidResult::ok({});
452}
static Result< T > ok(U &&value)
Create a successful result with value (static factory)
Definition core.h:223
std::unordered_map< std::type_index, service_entry > services_
static void log_event(const registry_event &event)
Log a registry event.
constexpr int already_registered
Service already registered (duplicate registration attempt)
Result< T > make_error(int code, const std::string &message, const std::string &module="")
Create an error result with code and message.
Definition utilities.h:91
static constexpr source_location current(const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE()) noexcept

References kcenon::common::di::di_error_codes::already_registered, kcenon::common::source_location::current(), kcenon::common::interfaces::RegistryAuditLog::log_event(), kcenon::common::make_error(), kcenon::common::Result< T >::ok(), kcenon::common::interfaces::register_service, and services_.

Referenced by register_factory_internal(), and register_instance_internal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_frozen_for_registration()

VoidResult kcenon::common::di::service_container::check_frozen_for_registration ( const std::string & type_name,
interfaces::registry_action action,
const std::string & error_message ) const
inlineprivate

Check if container is frozen and log audit event if so.

Parameters
type_nameThe type name for audit logging
actionThe registry action being performed
error_messageThe error message to use if frozen
Returns
VoidResult - error if frozen, ok otherwise

Definition at line 415 of file service_container.h.

418 {
419
420 if (is_frozen()) {
421 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
422 action, type_name,
424 "Container is frozen"));
427 error_message,
428 "di::service_container"
429 );
430 }
431
432 return VoidResult::ok({});
433}
bool is_frozen() const
Check if the container is frozen.
constexpr int REGISTRY_FROZEN
Definition compat.h:41

References kcenon::common::source_location::current(), is_frozen(), kcenon::common::interfaces::RegistryAuditLog::log_event(), kcenon::common::make_error(), kcenon::common::Result< T >::ok(), and kcenon::common::error_codes::REGISTRY_FROZEN.

Referenced by register_factory_internal(), and register_instance_internal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear()

VoidResult kcenon::common::di::service_container::clear ( )
inlineoverridevirtual

Clear all registrations.

Returns
VoidResult - error if the container is frozen, ok otherwise

Implements kcenon::common::di::IServiceContainer.

Definition at line 376 of file service_container.h.

376 {
377 if (is_frozen()) {
378 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
381 "Container is frozen"));
384 "Cannot clear services: container is frozen",
385 "di::service_container"
386 );
387 }
388
389 std::unique_lock lock(mutex_);
390 services_.clear();
391
392 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
395
396 return VoidResult::ok({});
397}

References kcenon::common::interfaces::clear_services, kcenon::common::source_location::current(), is_frozen(), kcenon::common::interfaces::RegistryAuditLog::log_event(), kcenon::common::make_error(), mutex_, kcenon::common::Result< T >::ok(), kcenon::common::error_codes::REGISTRY_FROZEN, and services_.

Referenced by kcenon::common::di::unified_bootstrapper::initialize(), and kcenon::common::di::unified_bootstrapper::shutdown().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ create_scope()

std::unique_ptr< IServiceScope > kcenon::common::di::service_container::create_scope ( )
inlineoverridevirtual

Create a new service scope.

Returns
Unique pointer to the new scope

Implements kcenon::common::di::IServiceContainer.

Definition at line 357 of file service_container.h.

357 {
358 return std::make_unique<service_scope>(*this);
359}

◆ freeze()

void kcenon::common::di::service_container::freeze ( )
inline

Freeze the container to prevent further registrations.

Once frozen, no new services can be registered or unregistered. Existing services can still be resolved. This is a one-way operation and cannot be undone.

Note
This should be called after system initialization to prevent unauthorized service replacement which could be used to inject malicious implementations.
This is a security feature to prevent service hijacking.
See also
Issue #206 for security requirements.

Definition at line 399 of file service_container.h.

399 {
400 frozen_.store(true, std::memory_order_release);
401
402 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
405}
@ freeze_service_container
Freeze service container.

References kcenon::common::source_location::current(), kcenon::common::interfaces::freeze_service_container, frozen_, and kcenon::common::interfaces::RegistryAuditLog::log_event().

Referenced by kcenon::common::bootstrap::SystemBootstrapper::initialize().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_resolution_stack_string()

std::string kcenon::common::di::service_container::get_resolution_stack_string ( ) const
inlineprivate

Get current resolution stack as string for error messages.

Definition at line 730 of file service_container.h.

730 {
731 std::string result;
732 for (size_t i = 0; i < resolution_order_.size(); ++i) {
733 if (i > 0) {
734 result += " -> ";
735 }
736 result += resolution_order_[i].name();
737 }
738 return result;
739}
static thread_local std::vector< std::type_index > resolution_order_

References resolution_order_.

Referenced by resolve_with_detection().

Here is the caller graph for this function:

◆ global()

service_container & kcenon::common::di::service_container::global ( )
inlinestatic

Get the global service container instance.

Returns a reference to the application-wide singleton container. Thread-safe for concurrent access.

Returns
Reference to the global service_container

Definition at line 352 of file service_container.h.

352 {
353 static service_container instance;
354 return instance;
355}
service_container()=default
Default constructor.

Referenced by kcenon::common::bootstrap::SystemBootstrapper::initialize(), kcenon::common::di::unified_bootstrapper::initialize(), main(), kcenon::common::di::unified_bootstrapper::register_module(), kcenon::common::di::unified_bootstrapper::register_optional_services(), kcenon::common::di::unified_bootstrapper::services(), and kcenon::common::di::unified_bootstrapper::shutdown().

Here is the caller graph for this function:

◆ invoke_factory_safe()

Result< std::shared_ptr< void > > kcenon::common::di::service_container::invoke_factory_safe ( const std::function< std::shared_ptr< void >(IServiceContainer &)> & factory)
inlineprivate

Safely invoke a factory function with exception handling.

Parameters
factoryThe factory function to invoke
Returns
Result containing the created instance or a factory_error

Definition at line 454 of file service_container.h.

455 {
456
457 try {
458 return Result<std::shared_ptr<void>>::ok(factory(*this));
459 } catch (const std::exception& e) {
462 std::string("Factory threw exception: ") + e.what(),
463 "di::service_container"
464 );
465 }
466}
constexpr int factory_error
Factory threw an exception during instantiation.
VoidResult ok()
Create a successful void result.
Definition utilities.h:71

References kcenon::common::di::di_error_codes::factory_error, kcenon::common::make_error(), and kcenon::common::ok().

Referenced by resolve_with_detection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_frozen()

bool kcenon::common::di::service_container::is_frozen ( ) const
inline

Check if the container is frozen.

Returns
true if the container is frozen and cannot be modified

Definition at line 407 of file service_container.h.

407 {
408 return frozen_.load(std::memory_order_acquire);
409}

References frozen_.

Referenced by check_frozen_for_registration(), clear(), and unregister_internal().

Here is the caller graph for this function:

◆ is_registered_internal()

bool kcenon::common::di::service_container::is_registered_internal ( std::type_index interface_type) const
inlineoverrideprotectedvirtual

Internal registration check (type-erased).

Implements kcenon::common::di::IServiceContainer.

Definition at line 670 of file service_container.h.

670 {
671 std::shared_lock lock(mutex_);
672 return services_.find(interface_type) != services_.end();
673}

References mutex_, and services_.

Referenced by kcenon::common::di::service_scope::is_registered_internal().

Here is the caller graph for this function:

◆ is_resolving()

bool kcenon::common::di::service_container::is_resolving ( std::type_index interface_type) const
inlineprivate

Check if currently resolving this type (circular dependency check).

Definition at line 714 of file service_container.h.

714 {
715 return resolution_stack_.find(interface_type) != resolution_stack_.end();
716}
static thread_local std::unordered_set< std::type_index > resolution_stack_

References resolution_stack_.

Referenced by resolve_with_detection().

Here is the caller graph for this function:

◆ operator=() [1/2]

service_container & kcenon::common::di::service_container::operator= ( const service_container & )
delete

◆ operator=() [2/2]

service_container & kcenon::common::di::service_container::operator= ( service_container && )
delete

◆ pop_resolution()

void kcenon::common::di::service_container::pop_resolution ( std::type_index interface_type)
inlineprivate

Pop type from resolution stack.

Definition at line 723 of file service_container.h.

723 {
724 resolution_stack_.erase(interface_type);
725 if (!resolution_order_.empty() && resolution_order_.back() == interface_type) {
726 resolution_order_.pop_back();
727 }
728}

References resolution_order_, and resolution_stack_.

Referenced by resolve_with_detection().

Here is the caller graph for this function:

◆ push_resolution()

void kcenon::common::di::service_container::push_resolution ( std::type_index interface_type)
inlineprivate

Push type onto resolution stack.

Definition at line 718 of file service_container.h.

718 {
719 resolution_stack_.insert(interface_type);
720 resolution_order_.push_back(interface_type);
721}

References resolution_order_, and resolution_stack_.

Referenced by resolve_with_detection().

Here is the caller graph for this function:

◆ register_factory_internal()

VoidResult kcenon::common::di::service_container::register_factory_internal ( std::type_index interface_type,
const std::string & type_name,
std::function< std::shared_ptr< void >(IServiceContainer &)> factory,
service_lifetime lifetime )
inlineoverrideprotectedvirtual

Internal factory registration (type-erased).

Implements kcenon::common::di::IServiceContainer.

Definition at line 468 of file service_container.h.

472 {
473
474 // Check frozen state before acquiring lock
475 auto frozen_check = check_frozen_for_registration(
476 type_name,
478 "Cannot register service: container is frozen");
479 if (!frozen_check.is_ok()) {
480 return frozen_check;
481 }
482
483 std::unique_lock lock(mutex_);
484
485 // Check if already registered (must hold lock)
486 auto registered_check = check_already_registered(interface_type, type_name);
487 if (!registered_check.is_ok()) {
488 return registered_check;
489 }
490
491 services_.emplace(
492 interface_type,
493 service_entry(interface_type, type_name, std::move(factory), lifetime)
494 );
495
496 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
499
500 return VoidResult::ok({});
501}
VoidResult check_already_registered(std::type_index interface_type, const std::string &type_name) const
Check if a service is already registered and log audit event if so.
VoidResult check_frozen_for_registration(const std::string &type_name, interfaces::registry_action action, const std::string &error_message) const
Check if container is frozen and log audit event if so.

References check_already_registered(), check_frozen_for_registration(), kcenon::common::source_location::current(), kcenon::common::interfaces::RegistryAuditLog::log_event(), mutex_, kcenon::common::Result< T >::ok(), kcenon::common::interfaces::register_service, and services_.

Referenced by kcenon::common::di::service_scope::register_factory_internal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ register_instance_internal()

VoidResult kcenon::common::di::service_container::register_instance_internal ( std::type_index interface_type,
const std::string & type_name,
std::shared_ptr< void > instance )
inlineoverrideprotectedvirtual

Internal instance registration (type-erased).

Implements kcenon::common::di::IServiceContainer.

Definition at line 503 of file service_container.h.

506 {
507
508 // Check frozen state before acquiring lock
509 auto frozen_check = check_frozen_for_registration(
510 type_name,
512 "Cannot register instance: container is frozen");
513 if (!frozen_check.is_ok()) {
514 return frozen_check;
515 }
516
517 std::unique_lock lock(mutex_);
518
519 // Check if already registered (must hold lock)
520 auto registered_check = check_already_registered(interface_type, type_name);
521 if (!registered_check.is_ok()) {
522 return registered_check;
523 }
524
525 // Create entry with instance as singleton
526 service_entry entry(
527 interface_type,
528 type_name,
529 [](IServiceContainer&) -> std::shared_ptr<void> { return nullptr; },
531 );
532 entry.singleton_instance = std::move(instance);
533 entry.is_instantiated = true;
534
535 services_.emplace(interface_type, std::move(entry));
536
537 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
540
541 return VoidResult::ok({});
542}

References check_already_registered(), check_frozen_for_registration(), kcenon::common::source_location::current(), kcenon::common::interfaces::RegistryAuditLog::log_event(), mutex_, kcenon::common::Result< T >::ok(), kcenon::common::interfaces::register_service, services_, kcenon::common::di::singleton, and kcenon::common::di::service_container::service_entry::singleton_instance.

Referenced by kcenon::common::di::service_scope::register_instance_internal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ registered_services()

std::vector< service_descriptor > kcenon::common::di::service_container::registered_services ( ) const
inlineoverridevirtual

Get list of all registered service descriptors.

Returns
Vector of service descriptors

Implements kcenon::common::di::IServiceContainer.

Definition at line 361 of file service_container.h.

361 {
362 std::shared_lock lock(mutex_);
363
364 std::vector<service_descriptor> result;
365 result.reserve(services_.size());
366
367 for (const auto& [type_index, entry] : services_) {
368 service_descriptor desc(entry.interface_type, entry.type_name, entry.lifetime);
369 desc.is_instantiated = entry.is_instantiated;
370 result.push_back(std::move(desc));
371 }
372
373 return result;
374}

References kcenon::common::di::service_descriptor::is_instantiated, mutex_, and services_.

Referenced by kcenon::common::di::service_scope::registered_services().

Here is the caller graph for this function:

◆ resolve_internal()

Result< std::shared_ptr< void > > kcenon::common::di::service_container::resolve_internal ( std::type_index interface_type)
inlineoverrideprotectedvirtual

Internal service resolution (type-erased).

Implements kcenon::common::di::IServiceContainer.

Definition at line 544 of file service_container.h.

545 {
546 return resolve_with_detection(interface_type, nullptr);
547}
Result< std::shared_ptr< void > > resolve_with_detection(std::type_index interface_type, std::unordered_map< std::type_index, std::shared_ptr< void > > *scoped_instances=nullptr)
Resolve a service with circular dependency detection.

References resolve_with_detection().

Here is the call graph for this function:

◆ resolve_with_detection()

Result< std::shared_ptr< void > > kcenon::common::di::service_container::resolve_with_detection ( std::type_index interface_type,
std::unordered_map< std::type_index, std::shared_ptr< void > > * scoped_instances = nullptr )
inlineprivate

Resolve a service with circular dependency detection.

Parameters
interface_typeThe type to resolve
scoped_instancesOptional map for scoped instances (from scope)
Returns
Result containing the service or an error

Definition at line 549 of file service_container.h.

551 {
552
553 // Check for circular dependency
554 if (is_resolving(interface_type)) {
555 std::string cycle_info = get_resolution_stack_string();
558 "Circular dependency detected: " + cycle_info,
559 "di::service_container"
560 );
561 }
562
563 // Check if service is registered
564 {
565 std::shared_lock lock(mutex_);
566 auto it = services_.find(interface_type);
567 if (it == services_.end()) {
570 "Service not registered: " + std::string(interface_type.name()),
571 "di::service_container"
572 );
573 }
574 }
575
576 // Push onto resolution stack
577 push_resolution(interface_type);
578
579 // RAII guard to pop from resolution stack
580 struct resolution_guard {
581 service_container* container;
582 std::type_index type;
583 ~resolution_guard() { container->pop_resolution(type); }
584 } guard{this, interface_type};
585
586 // Resolve based on lifetime
587 std::shared_lock read_lock(mutex_);
588 auto it = services_.find(interface_type);
589 auto& entry = it->second;
590
591 // Copy factory and lifetime before releasing lock to avoid deadlock
592 // when factory calls resolve() recursively
593 auto factory_copy = entry.factory;
594 auto lifetime = entry.lifetime;
595
596 switch (lifetime) {
598 // Double-checked locking for singleton
599 if (entry.is_instantiated) {
600 return Result<std::shared_ptr<void>>::ok(entry.singleton_instance);
601 }
602
603 read_lock.unlock();
604
605 // Create instance outside of lock to avoid deadlock
606 auto factory_result = invoke_factory_safe(factory_copy);
607 if (!factory_result.is_ok()) {
608 return factory_result;
609 }
610
611 // Now acquire write lock to store the instance
612 std::unique_lock write_lock(mutex_);
613
614 // Re-check after acquiring write lock (another thread may have created it)
615 auto it2 = services_.find(interface_type);
616 auto& entry2 = it2->second;
617 if (entry2.is_instantiated) {
618 // Another thread created it, return that instance
619 return Result<std::shared_ptr<void>>::ok(entry2.singleton_instance);
620 }
621
622 entry2.singleton_instance = std::move(factory_result.value());
623 entry2.is_instantiated = true;
624 return Result<std::shared_ptr<void>>::ok(entry2.singleton_instance);
625 }
626
628 // Create new instance each time - release lock before calling factory
629 read_lock.unlock();
630 return invoke_factory_safe(factory_copy);
631 }
632
634 // Scoped services should be resolved from a scope
635 if (scoped_instances == nullptr) {
638 "Cannot resolve scoped service from root container. Use create_scope().",
639 "di::service_container"
640 );
641 }
642
643 // Check if already instantiated in scope
644 auto scoped_it = scoped_instances->find(interface_type);
645 if (scoped_it != scoped_instances->end()) {
646 return Result<std::shared_ptr<void>>::ok(scoped_it->second);
647 }
648
649 // Create new instance for scope - release lock before calling factory
650 read_lock.unlock();
651 auto factory_result = invoke_factory_safe(factory_copy);
652 if (!factory_result.is_ok()) {
653 return factory_result;
654 }
655
656 auto instance = std::move(factory_result.value());
657 (*scoped_instances)[interface_type] = instance;
658 return Result<std::shared_ptr<void>>::ok(std::move(instance));
659 }
660
661 default:
664 "Unknown service lifetime",
665 "di::service_container"
666 );
667 }
668}
bool is_resolving(std::type_index interface_type) const
Check if currently resolving this type (circular dependency check).
std::string get_resolution_stack_string() const
Get current resolution stack as string for error messages.
void push_resolution(std::type_index interface_type)
Push type onto resolution stack.
Result< std::shared_ptr< void > > invoke_factory_safe(const std::function< std::shared_ptr< void >(IServiceContainer &)> &factory)
Safely invoke a factory function with exception handling.
constexpr int scoped_from_root
Scoped service resolved from root container.
constexpr int circular_dependency
Circular dependency detected during resolution.
constexpr int invalid_lifetime
Invalid service lifetime configuration.
constexpr int service_not_registered
Service not registered in container.
@ scoped
Single instance within a scope.

References kcenon::common::di::di_error_codes::circular_dependency, get_resolution_stack_string(), kcenon::common::di::di_error_codes::invalid_lifetime, invoke_factory_safe(), is_resolving(), kcenon::common::make_error(), mutex_, kcenon::common::ok(), pop_resolution(), push_resolution(), kcenon::common::di::scoped, kcenon::common::di::di_error_codes::scoped_from_root, kcenon::common::di::di_error_codes::service_not_registered, services_, kcenon::common::di::singleton, and kcenon::common::di::transient.

Referenced by resolve_internal(), and kcenon::common::di::service_scope::resolve_internal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unregister_internal()

VoidResult kcenon::common::di::service_container::unregister_internal ( std::type_index interface_type)
inlineoverrideprotectedvirtual

Internal unregistration (type-erased).

Implements kcenon::common::di::IServiceContainer.

Definition at line 675 of file service_container.h.

675 {
676 std::string type_name = interface_type.name();
677
678 if (is_frozen()) {
679 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
682 "Container is frozen"));
685 "Cannot unregister service: container is frozen",
686 "di::service_container"
687 );
688 }
689
690 std::unique_lock lock(mutex_);
691
692 auto it = services_.find(interface_type);
693 if (it == services_.end()) {
694 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
697 "Service not registered"));
700 "Service not registered",
701 "di::service_container"
702 );
703 }
704
705 services_.erase(it);
706
707 interfaces::RegistryAuditLog::log_event(interfaces::registry_event(
710
711 return VoidResult::ok({});
712}
@ unregister_service
Service unregistration.

References kcenon::common::source_location::current(), is_frozen(), kcenon::common::interfaces::RegistryAuditLog::log_event(), kcenon::common::make_error(), mutex_, kcenon::common::Result< T >::ok(), kcenon::common::error_codes::REGISTRY_FROZEN, kcenon::common::di::di_error_codes::service_not_registered, services_, and kcenon::common::interfaces::unregister_service.

Referenced by kcenon::common::di::service_scope::unregister_internal().

Here is the call graph for this function:
Here is the caller graph for this function:

Friends And Related Symbol Documentation

◆ service_scope

friend class service_scope
friend

Definition at line 161 of file service_container.h.

Member Data Documentation

◆ frozen_

std::atomic<bool> kcenon::common::di::service_container::frozen_ {false}
private

Definition at line 256 of file service_container.h.

256{false};

Referenced by freeze(), and is_frozen().

◆ mutex_

std::shared_mutex kcenon::common::di::service_container::mutex_
mutableprivate

◆ resolution_order_

thread_local std::vector< std::type_index > kcenon::common::di::service_container::resolution_order_
inlinestaticprivate

◆ resolution_stack_

thread_local std::unordered_set< std::type_index > kcenon::common::di::service_container::resolution_stack_
inlinestaticprivate

Definition at line 259 of file service_container.h.

Referenced by is_resolving(), pop_resolution(), and push_resolution().

◆ services_

std::unordered_map<std::type_index, service_entry> kcenon::common::di::service_container::services_
private

The documentation for this class was generated from the following file: