-
Notifications
You must be signed in to change notification settings - Fork 39
07. Autocall
Sometime only constructing a service is not enough. Some classes needs configuration, or some function to be called initially, or even call some setters.
The autocall feature is doing exactly that. It's a list of function to call upon the construction of a service. The container will invoke all function in that list. You can use this feature to perform injection through setters.
First of all, we recommend defining a shortcut for using kgr::invoke
. Of you have C++17 enabled, you can define that shortcut like that:
template<auto m>
using method = kgr::method<decltype(m), m>;
Alternatively, if you only have C++11 or C++14 on hand, you can use a macro:
#define METHOD(...) ::kgr::method<decltype(__VA_ARGS__), __VA_ARGS__>
Of course, you are free to name them as you want. Once your shotcut is declared, let's get started!
Any service can extends the class kgr::autocall
. This class enable the needed metadata for the container to call the methods you want to be called.
The kgr::autocall
type is a list of method to call in your class. More specifically, a list of kgr::method
.
Let's see an example of it's usage. So we have this class:
struct MessageBus {
void init() {
max_delay = 42;
}
private:
int max_delay;
};
If we want init()
to be called at the service's construction, we need our definition to extends kgr::autocall
:
struct MessageBusService : kgr::service<MessageBus>, kgr::autocall<METHOD(&MessageBus::init)> {};
Great! Now creating the service will call that function:
// MessageBus::init is called before returning
MessageBus md = container.service<MessageBusService>();
Here comes the fancy thing. Functions listed in kgr::autocall
can receive other services.
In fact, the container will call these functions using the invoke
function. So you can receive any other services.
For example, we need max_delay
to be calculated with values that comes from other service.
So here's our class according to the new need:
struct MessageBus {
void init(Window& window) {
max_delay = 3 * window.get_framerate();
}
private:
int max_delay;
};
That's it! You can add any number of parameter as you wish, the definition will stay the same and the method will receive what you ask for.
As said before, kgr::autocall
is a list of method to call. You can have as many method to call as you wish
struct MessageBus {
void init(Window& window, Camera& camera) {
max_delay = 3 * window.get_framerate();
}
void set_scene(Scene& scene) {
this->scene = &scene;
}
private:
Scene* scene;
int max_delay;
};
struct MessageBusService : kgr::service<MessageBus>, kgr::autocall<
METHOD(&MessageBus::init),
METHOD(&MessageBus::set_scene)
> {};
Non-member function are also supported. Here's the same code as above, using non-member functions:
struct MessageBus {
Scene* scene;
int max_delay;
};
void init(MessageBus& self, Window& window, Camera& camera) {
self.max_delay = 3 * window.get_framerate();
}
void set_scene(MessageBus& self, Scene& scene) {
self.scene = &scene;
}
struct MessageBusService : kgr::service<MessageBus>, kgr::autocall<
METHOD(init),
METHOD(set_scene)
> {};
The functions are called in the order that are listed in kgr::autocall
.
In previous examples, we used the default service map. If you deal with advanced mapping, you might want to specity which maps to use. You can set the default map to use in the first parameter of autocall:
struct MyMap;
struct MessageBusService : kgr::service<MessageBus>, kgr::autocall<
kgr::map<MyMap1, MyMap2>,
METHOD(&MessageBus::init),
METHOD(&MessageBus::set_scene)
> {};
Alternatively, you can list needed services for every methods. Parameters are grouped within the kgr::invoke
class:
struct MessageBusService : kgr::service<MessageBus>, kgr::autocall<
kgr::invoke<METHOD(&MessageBus::init), WindowService, CameraService>,
METHOD(&MessageBus::set_scene)
> {};
To see those snippets in action, go check example6.