EnTT: Crash Course: service locator

EnTT 3.16.0

Loading...
Searching...
No Matches
Crash Course: service locator

# Table of Contents

- Introduction
- Service locator
  - Opaque handles

# Introduction

Usually, service locators are tightly bound to the services they expose. It is hard to define a general purpose solution.
This tiny class tries to fill the gap and gets rid of the burden of defining a different specific locator for each application.

# Service locator

The service locator API tries to mimic that of std::optional and adds some extra functionalities on top of it such as allocator support.
There are a couple of functions to set up a service, namely emplace and allocate_emplace:

entt::locator<interface>::emplace<service>(argument);
entt::locator<interface>::allocate_emplace<service>(allocator, argument);
entt::locator
Service locator, nothing more.
Definition locator.hpp:27
entt::locator::emplace
static Service & emplace(Args &&...args)
Sets or replaces a service.
Definition locator.hpp:100

The difference is that the latter expects an allocator as the first argument and uses it to allocate the service itself.
Once a service is set up, it is retrieved using the value function:

interface &service = entt::locator<interface>::value();
entt::locator::value
static Service & value() noexcept
Returns a reference to a valid service, if any.
Definition locator.hpp:71

Since the service may not be set (and therefore this function may result in an undefined behavior), the has_value and value_or functions are also available to test a service locator and to get a fallback service in case there is none:

if(entt::locator<interface>::has_value()) {
// ...
}
interface &service = entt::locator<interface>::value_or<fallback_impl>(argument);
entt::locator::has_value
static bool has_value() noexcept
Checks whether a service locator contains a value.
Definition locator.hpp:58
entt::locator::value_or
static Service & value_or(Args &&...args)
Returns a service if available or sets it from a fallback type.
Definition locator.hpp:88

All arguments are used only if necessary, that is, if a service does not already exist and therefore the fallback service is constructed and returned. In all other cases, they are discarded.
Finally, to reset a service, use the reset function.

## Opaque handles

Sometimes it is useful to transfer a copy of a service to another locator. For example, when working across boundaries it is common to share a service with a dynamically loaded module.
Options are not much in this case. Among these is the possibility of exporting services and assigning them to a different locator.

This is what the handle and reset functions are meant for.
The former returns an opaque object useful for exporting (or rather, obtaining a reference to) a service. The latter also accepts an optional argument to a handle which then allows users to reset a service by initializing it with an opaque handle:

auto handle = entt::locator<interface>::handle();
entt::locator<interface>::reset(handle);
entt::locator::handle
static node_type handle() noexcept
Returns a handle to the underlying service.
Definition locator.hpp:124
entt::locator::reset
static void reset(const node_type &other={}) noexcept
Resets or replaces a service.
Definition locator.hpp:134

It is worth noting that it is possible to get handles for uninitialized services and use them with other locators. Of course, all a user will get is to have an uninitialized service elsewhere as well.

Note that exporting a service allows users to share the object currently set in a locator. Replacing it will not replace the element, even where a service has been configured with a handle to the previous item.
In other words, if an audio service is replaced with a null object to silence an application and the original service was shared, this operation will not propagate to the other locators. Therefore, a module that shares the ownership of the original audio service is still able to emit sounds.