With this redesign, we introduce a new way to create plugins, which is easier to extend with new hook points, provides a better interface, uses less memory, less program space, and is a tiny bit faster too. It all begins with `kaleidoscope::Plugin` being the base class, which provides the hook methods plugins can implement. Plugins should be declared with `KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro is a bit of magic (see the in-code documentation) that allows us to unroll the hook method calls, avoid vtables, and so on. It creates an override for `kaleidoscope::Hooks::*` methods, each of which will call the respective methods of each initialized plugin. With the new API come new names: all of the methods plugins can implement received new, more descriptive names that all follow a similar pattern. The old (dubbed V1) API still remains in place, although deprecated. One can turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero, while compiling the firmware. This work is based on #276, written by @noseglasses. @obra and @algernon did some cleaning up and applied a little naming treatment. Signed-off-by: noseglasses <shinynoseglasses@gmail.com> Signed-off-by: Jesse Vincent <jesse@keyboard.io> Signed-off-by: Gergely Nagy <algernon@keyboard.io>pull/316/head
parent
bc76032d37
commit
8130dfdf1d
@ -0,0 +1,114 @@
|
|||||||
|
Deprecated APIs and their replacements
|
||||||
|
======================================
|
||||||
|
|
||||||
|
As the firmware evolves, there are - and will be - APIs that we deprecate, and
|
||||||
|
eventually remove. This document is a short guide that lists the deprecations -
|
||||||
|
along with when it happened, and when the functionality will be removed -,
|
||||||
|
suggested upgrade paths, and any other information that may be useful.
|
||||||
|
|
||||||
|
If any of this does not make sense to you, or you have trouble updating your
|
||||||
|
.ino sketch, do not hesitate to write us at help@keyboard.io, we can help you
|
||||||
|
fix it.
|
||||||
|
|
||||||
|
## Sheduled for removal by 2018-05-26
|
||||||
|
|
||||||
|
These APIs and functions have been deprecated for a long time, and as far as we
|
||||||
|
can tell, aren't used by any third party or user code anymore.
|
||||||
|
|
||||||
|
### Kaleidoscope.setup(KEYMAP_SIZE)
|
||||||
|
|
||||||
|
The `Kaleidoscope.setup()` method is still around, and is **not** deprecated,
|
||||||
|
but the variant of it that takes a keymap size is, and has been since October
|
||||||
|
2017.
|
||||||
|
|
||||||
|
Instead, one should use the argument-less `Kaleidoscope.setup()`, and the new
|
||||||
|
`KEYMAP()` macros to define a keymap.
|
||||||
|
|
||||||
|
### event_handler_hook_use, loop_hook_use, and USE_PLUGINS
|
||||||
|
|
||||||
|
Deprecated in October 2017, these are old aliases that should no longer be in
|
||||||
|
use. They were replaced by `Kaleidoscope.useEventHandlerHook`,
|
||||||
|
`Kaleidoscope.useLoopHook`, and `Kaleidoscope.use`, respectively.
|
||||||
|
|
||||||
|
The replacements themselves are also deprecated - see below -, but their removal
|
||||||
|
will come at a later date.
|
||||||
|
|
||||||
|
### MOMENTARY_OFFSET
|
||||||
|
|
||||||
|
Deprecated in October 2017, replaced by `LAYER_SHIFT_OFFSET`.
|
||||||
|
|
||||||
|
This symbol was meant to be used by plugins, not user code, and as far as we
|
||||||
|
know, no third party plugin ever used it.
|
||||||
|
|
||||||
|
### key_was_pressed, key_is_pressed, key_toggled_on, key_toggled_off
|
||||||
|
|
||||||
|
Deprecated in July 2017, replaced by `keyWasPressed`, `keyIsPressed`,
|
||||||
|
`keyToggledOn`, and `keyToggledOff`, respectively.
|
||||||
|
|
||||||
|
## Sheduled for removal by 2018-07-12
|
||||||
|
|
||||||
|
We aim at making a new release by mid-July, and APIs we deprecate now, will be
|
||||||
|
removed shortly before the major release. We may deprecate further APIs during
|
||||||
|
the next month (until mid-June), and those deprecations will share the same
|
||||||
|
removal date. We will try our best to minimize deprecations, and do them as soon
|
||||||
|
as possible, to give everyone at least a month to prepare and update.
|
||||||
|
|
||||||
|
### Kaleidoscope.use()
|
||||||
|
|
||||||
|
Deprecated in May 2018, this method is part of the old plugin API, replaced by
|
||||||
|
`KALEIDOSCOPE_INIT_PLUGINS`. To upgrade, you need to modify your .ino sketch
|
||||||
|
file, and replace the text `Kaleidoscope.use` with `KALEIDOSCOPE_INIT_PLUGINS`,
|
||||||
|
then remove the `&` from all of the plugins inside it, and finally, move it
|
||||||
|
outside of `setup()`.
|
||||||
|
|
||||||
|
If your current sketch looks like this:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void setup() {
|
||||||
|
Kaleidoscope.use(&Plugin1, &Plugin2);
|
||||||
|
Kaleidoscope.setup();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You should change it so that it looks like this instead:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
KALEIDOSCOPE_INIT_PLUGINS(Plugin1, Plugin2);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Kaleidoscope.setup();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### The old-style (v1) plugin API
|
||||||
|
|
||||||
|
This includes using `KaleidoscopePlugin`, `Kaleidoscope.useEventHandlerHook`,
|
||||||
|
`Kaleidoscope.replaceEventHandlerHook`, `Kaleidoscope.appendEventHandlerHook`,
|
||||||
|
`Kaleidoscope.useLoopHook`, `Kaleidoscope.replaceLoopHook`,
|
||||||
|
`Kaleidoscope.appendLoopHook`. They were deprecated in May 2017.
|
||||||
|
|
||||||
|
Their replacement is the new plugin API:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
namespace kaleidoscope {
|
||||||
|
|
||||||
|
enum class EventHandlerResult {
|
||||||
|
OK,
|
||||||
|
EVENT_CONSUMED,
|
||||||
|
ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Plugin {
|
||||||
|
public:
|
||||||
|
EventHandlerResult onSetup();
|
||||||
|
EventHandlerResult beforeEachCycle();
|
||||||
|
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state);
|
||||||
|
EventHandlerResult beforeReportingState();
|
||||||
|
EventHandlerResult afterEachCycle();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Plugins are supposed to implement this new API, and then be initialised via
|
||||||
|
`KALEIDOSCOPE_INIT_PLUGINS`.
|
@ -0,0 +1,13 @@
|
|||||||
|
# Glossary
|
||||||
|
|
||||||
|
This document is intended to name and describe the concepts, functions and data structures inside Kaleidoscope.
|
||||||
|
|
||||||
|
It is, as yet, incredibly incomplete.
|
||||||
|
|
||||||
|
Entries should be included in dictionary order. When describing an identifier of any kind from the codebase, it should be
|
||||||
|
written using identical capitalization to its use in the code and surrounded by backticks: `identifierName`
|
||||||
|
|
||||||
|
|
||||||
|
Event handler
|
||||||
|
|
||||||
|
: A function, usually provided by a `Plugin` that is run by a `Hook`
|
@ -0,0 +1,342 @@
|
|||||||
|
# Kaleidoscope Plugin API Internals
|
||||||
|
|
||||||
|
This document attempts to explain how the plugin system works internally, behind
|
||||||
|
the scenes. We feel this is necessary, because there are some unorthodox
|
||||||
|
solutions in play, which make the code incredibly hard to untangle. This is an
|
||||||
|
unavoidable side-effect of employing a system that lets us use non-virtual
|
||||||
|
functions, and save large amounts of RAM thereby. It would be a lot simpler
|
||||||
|
without this feature, but alas, saving hundreds of bytes of RAM is something we
|
||||||
|
felt is worth the complexity.
|
||||||
|
|
||||||
|
Lets start at the top:
|
||||||
|
|
||||||
|
## `KALEIDOSCOPE_INIT_PLUGINS`
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#define KALEIDOSCOPE_INIT_PLUGINS(...) _KALEIDOSCOPE_INIT_PLUGINS(__VA_ARGS__)
|
||||||
|
```
|
||||||
|
|
||||||
|
So far so good, this is pretty simple. The reason we use an indirection here is
|
||||||
|
because this is in `Kaleidoscope.h`, and we do not want the complexity of the
|
||||||
|
`_KALEIDOSCOPE_INIT_PLUGINS` macro here, nor do we want to move the macro to
|
||||||
|
another header, because it belongs to `Kaleidoscope.h`.
|
||||||
|
|
||||||
|
## `_KALEIDOSCOPE_INIT_PLUGINS`
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#define _KALEIDOSCOPE_INIT_PLUGINS(...) \
|
||||||
|
namespace kaleidoscope_internal { \
|
||||||
|
struct EventDispatcher { \
|
||||||
|
\
|
||||||
|
template<typename EventHandler__, typename... Args__ > \
|
||||||
|
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) { \
|
||||||
|
\
|
||||||
|
kaleidoscope::EventHandlerResult result; \
|
||||||
|
MAP(_INLINE_EVENT_HANDLER_FOR_PLUGIN, __VA_ARGS__) \
|
||||||
|
\
|
||||||
|
return result; \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
} \
|
||||||
|
_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER)
|
||||||
|
```
|
||||||
|
|
||||||
|
This is where things get interesting. This macro does two things:
|
||||||
|
|
||||||
|
- It creates `kaleidoscope_internal::EventDispatcher`, a class with a single
|
||||||
|
method, `apply`. This is a templated method, the template argument is the
|
||||||
|
method `apply` will call. Thus, `EventDispatcher::template apply<foo>` will
|
||||||
|
resolve to a function that calls the `foo` method of each plugin we listed
|
||||||
|
for `KALEIDOSCOPE_INIT_PLUGINS`. We'll see in a bit how this happens.
|
||||||
|
|
||||||
|
- The other part creates overrides for the `Kaleidoscope::Hooks::` family of
|
||||||
|
functions. These are wrappers around `EventDispatcher::template apply<foo>`.
|
||||||
|
We have these so higher level code would not need to care about the
|
||||||
|
implementation details, so that it can invoke the hooks as if they were
|
||||||
|
ordinary functions.
|
||||||
|
|
||||||
|
## `_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER)`
|
||||||
|
|
||||||
|
Lets look at `_FOR_EACH_EVENT_HANDLER` and `_REGISTER_EVENT_HANDLER` first,
|
||||||
|
because that's easier to explain, and does not lead down another rabbit hole.
|
||||||
|
|
||||||
|
### `_REGISTER_EVENT_HANDLER`
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#define _REGISTER_EVENT_HANDLER( \
|
||||||
|
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) \
|
||||||
|
\
|
||||||
|
namespace kaleidoscope_internal { \
|
||||||
|
\
|
||||||
|
struct EventHandler_##HOOK_NAME { \
|
||||||
|
\
|
||||||
|
static bool shouldAbortOnConsumedEvent() { \
|
||||||
|
return SHOULD_ABORT_ON_CONSUMED_EVENT; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
template<typename Plugin__, typename... Args__> \
|
||||||
|
static kaleidoscope::EventHandlerResult \
|
||||||
|
call(Plugin__ &plugin, Args__&&... hook_args) { \
|
||||||
|
_VALIDATE_EVENT_HANDLER_SIGNATURE(HOOK_NAME, Plugin__) \
|
||||||
|
return plugin.HOOK_NAME(hook_args...); \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
namespace kaleidoscope { \
|
||||||
|
\
|
||||||
|
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { \
|
||||||
|
return kaleidoscope_internal::EventDispatcher::template \
|
||||||
|
apply<kaleidoscope_internal::EventHandler_ ## HOOK_NAME> \
|
||||||
|
ARGS_LIST; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This looks big and scary, but in practice, it isn't all that bad. Nevertheless,
|
||||||
|
this is where the magic happens!
|
||||||
|
|
||||||
|
We create two things: `EventHandler_SomeThing` and `Hooks::SomeThing`, the
|
||||||
|
latter being a wrapper around the first, that uses `EventDispatcher::template
|
||||||
|
apply<>` discussed above.
|
||||||
|
|
||||||
|
Lets take `onSetup` as an example! For that, the above expands to:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
namespace kaleidoscope_internal {
|
||||||
|
|
||||||
|
struct EventHandler_onSetup {
|
||||||
|
static bool shouldAbortOnConsumedEvent() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Plugin__, typename... Args__>
|
||||||
|
static kaleidoscope::EventHandlerResult
|
||||||
|
call(Plugin__ &plugin, Args__&&... hook_args) {
|
||||||
|
return plugin.onSetup(hook_args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
|
||||||
|
EventHandlerResult Hooks::onSetup() {
|
||||||
|
return kaleidoscope_internal::EventDispatcher::template
|
||||||
|
apply<kaleidoscope_internal::EventHandler_onSetup>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This still looks scary... but please read a bit further, and it will all make
|
||||||
|
sense!
|
||||||
|
|
||||||
|
### `_FOR_EACH_EVENT_HANDLER`
|
||||||
|
|
||||||
|
This just evaluates its argument for each event handler supported by
|
||||||
|
Kaleidoscope core. Very simple macro expansion, which we will not expand here,
|
||||||
|
because that would take up a lot of space, and they all look the same (see
|
||||||
|
above).
|
||||||
|
|
||||||
|
## `EventDispatcher`
|
||||||
|
|
||||||
|
```c++
|
||||||
|
namespace kaleidoscope_internal {
|
||||||
|
struct EventDispatcher {
|
||||||
|
template<typename EventHandler__, typename... Args__ >
|
||||||
|
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) {
|
||||||
|
|
||||||
|
kaleidoscope::EventHandlerResult result;
|
||||||
|
MAP(_INLINE_EVENT_HANDLER_FOR_PLUGIN, __VA_ARGS__)
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This is where the other part of the magic happens, and we need to understand
|
||||||
|
this to be able to make sense of `_REGISTER_EVENT_HANDLER` above.
|
||||||
|
|
||||||
|
### `_INLINE_EVENT_HANDLER_FOR_PLUGIN`
|
||||||
|
|
||||||
|
This in isolation, is not very interesting, and is closely tied to
|
||||||
|
`EventDispatcher`. The definition is here so we can look at it while we learn
|
||||||
|
the details of `EventDispatcher` below.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#define _INLINE_EVENT_HANDLER_FOR_PLUGIN(PLUGIN) \
|
||||||
|
\
|
||||||
|
result = EventHandler__::call(PLUGIN, hook_args...); \
|
||||||
|
\
|
||||||
|
if (EventHandler__::shouldAbortOnConsumedEvent() && \
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) { \
|
||||||
|
return result; \
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Back to `EventDispatcher`...
|
||||||
|
|
||||||
|
The `EventDispatcher` structure has a single method: `apply<>`, which needs an
|
||||||
|
event handler as its template argument. What the macro does, is call the event
|
||||||
|
handler given in the template argument for each and every initialised plugin.
|
||||||
|
It's best explained with an example! Lets use two plugins, `SomePlugin` and
|
||||||
|
`ExampleEffect`:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
namespace kaleidoscope_internal {
|
||||||
|
struct EventDispatcher {
|
||||||
|
template<typename EventHandler__, typename... Args__ >
|
||||||
|
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) {
|
||||||
|
|
||||||
|
kaleidoscope::EventHandlerResult result;
|
||||||
|
|
||||||
|
result = EventHandler__::call(SomePlugin);
|
||||||
|
if (EventHandler__::shouldAbortOnConsumedEvent() &&
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = EventHandler__::call(ExampleEffect);
|
||||||
|
if (EventHandler__::shouldAbortOnConsumedEvent() &&
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
See? It's unrolled! But how do we get from here to - say - calling the
|
||||||
|
`onSetup()` method of the plugins? Why, by way of `EventDispatcher::template
|
||||||
|
apply<EventHandler_onSetup>`! Lets see what happens when we do a call like that:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
namespace kaleidoscope_internal {
|
||||||
|
struct EventDispatcher {
|
||||||
|
template<typename EventHandler_onSetup, typename... Args__ >
|
||||||
|
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) {
|
||||||
|
|
||||||
|
kaleidoscope::EventHandlerResult result;
|
||||||
|
|
||||||
|
result = EventHandler_onSetup::call(SomePlugin);
|
||||||
|
if (EventHandler_onSetup::shouldAbortOnConsumedEvent() &&
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = EventHandler_onSetup::call(ExampleEffect);
|
||||||
|
if (EventHandler_onSetup::shouldAbortOnConsumedEvent() &&
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Because we call `EventHandler_onSetup::call` with the plugin as the first
|
||||||
|
argument, and because `call` is also a templated function, where the first
|
||||||
|
argument is templated, we get a method that is polimorphic on its first
|
||||||
|
argument. Meaning, for each and every plugin, we'll have a matching
|
||||||
|
`EventHandler_onSetup::call`, that is tied to that plugin. *This* is the magic
|
||||||
|
that lets us use non-virtual methods!
|
||||||
|
|
||||||
|
## Exploring what the compiler does
|
||||||
|
|
||||||
|
Because all hooks are called via `kaleidoscope::Hooks::NAME`, lets explore how
|
||||||
|
the compiler will optimize the code for `onSetup`, assuming we use two plugins,
|
||||||
|
`SomePlugin` and `ExampleEffect`.
|
||||||
|
|
||||||
|
Our entry point is this:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
return kaleidoscope::Hooks::onSetup();
|
||||||
|
```
|
||||||
|
|
||||||
|
`_REGISTER_EVENT_HANDLER` created `Hooks::onSetup()` for us:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
EventHandlerResult kaleidoscope::Hooks::onSetup() {
|
||||||
|
return kaleidoscope_internal::EventDispatcher::template
|
||||||
|
apply<kaleidoscope_internal::EventHandler_onSetup>();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we inline the call to `EventDispatcher::template apply<>`, we end up with the
|
||||||
|
following:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
EventHandlerResult kaleidoscope::Hooks::onSetup() {
|
||||||
|
kaleidoscope::EventHandlerResult result;
|
||||||
|
|
||||||
|
result = EventHandler_onSetup::call(SomePlugin);
|
||||||
|
if (EventHandler_onSetup::shouldAbortOnConsumedEvent() &&
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = EventHandler_onSetup::call(ExampleEffect);
|
||||||
|
if (EventHandler_onSetup::shouldAbortOnConsumedEvent() &&
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is starting to look human readable, doesn't it? But we can go further,
|
||||||
|
because `EventHandler::onSetup::call` and
|
||||||
|
`EventHandler_onSetup::shouldAbortOnConsumedEvent` are evaluated at compile-time
|
||||||
|
too!
|
||||||
|
|
||||||
|
```c++
|
||||||
|
EventHandlerResult kaleidoscope::Hooks::onSetup() {
|
||||||
|
kaleidoscope::EventHandlerResult result;
|
||||||
|
|
||||||
|
result = SomePlugin.onSetup();
|
||||||
|
if (false &&
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ExampleEffect.onSetup();
|
||||||
|
if (false &&
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Which in turn, may be optimized further to something like the following:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
EventHandlerResult kaleidoscope::Hooks::onSetup() {
|
||||||
|
kaleidoscope::EventHandlerResult result;
|
||||||
|
|
||||||
|
result = SomePlugin.onSetup();
|
||||||
|
result = ExampleEffect.onSetup();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And this, is the end of the magic. This is roughly how much the code gets
|
||||||
|
transformed *at compile time*, so that at run-time, none of this indirection is
|
||||||
|
present.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
As we can see, there is a lot going on behind the scenes, and a combination of
|
||||||
|
template meta programming and pre-processor macros is used to accomplish our
|
||||||
|
goal. But following the code path like we did above allows us to see what the
|
||||||
|
compiler sees (more or less), and inlining all the things that are done
|
||||||
|
compile-time gives us the final code, which is pretty simple by that point.
|
@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This file defines the names and argument signatures for all event handlers
|
||||||
|
// in the Kaleidoscope core.
|
||||||
|
//
|
||||||
|
// When adding a new hook or event handler to Kaleidoscope, it needs only
|
||||||
|
// to be added here and to the place in the codebase where it should be called.
|
||||||
|
//
|
||||||
|
// _FOR_EACH_EVENT_HANDLER is what's called an "X Macro". Its first argument,
|
||||||
|
// OPERATION, is the name of another macro. When called, it will execute
|
||||||
|
// OPERATION once for each event handler defined herein with the following
|
||||||
|
// parameters:
|
||||||
|
//
|
||||||
|
// HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST, ...
|
||||||
|
//
|
||||||
|
// Any additional parameters that are added to an invokation
|
||||||
|
// of _FOR_EACH_EVENT_HANDLER are passed through to OP.
|
||||||
|
|
||||||
|
#define _ABORTABLE true
|
||||||
|
#define _NOT_ABORTABLE false
|
||||||
|
|
||||||
|
#define _FOR_EACH_EVENT_HANDLER(OPERATION, ...) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
OPERATION(onSetup, __NL__ \
|
||||||
|
_NOT_ABORTABLE, __NL__ \
|
||||||
|
(),(), ##__VA_ARGS__) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
/* Called at the very start of each cycle, before gathering */ __NL__ \
|
||||||
|
/* events, before doing anything else. */ __NL__ \
|
||||||
|
OPERATION(beforeEachCycle, __NL__ \
|
||||||
|
_NOT_ABORTABLE, __NL__ \
|
||||||
|
(), (), ##__VA_ARGS__) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
/* Function called for every non-idle key, every cycle, so it */ __NL__ \
|
||||||
|
/* can decide what to do with it. It can modify the key (which is */ __NL__ \
|
||||||
|
/* passed by reference for this reason), and decide whether */ __NL__ \
|
||||||
|
/* further handles should be tried. If it returns */ __NL__ \
|
||||||
|
/* EventHandlerResult::OK, other handlers will also get a chance */ __NL__ \
|
||||||
|
/* to react to the event. If it returns anything else, Kaleidoscope */ __NL__ \
|
||||||
|
/* will stop processing there. */ __NL__ \
|
||||||
|
OPERATION(onKeyswitchEvent, __NL__ \
|
||||||
|
_ABORTABLE, __NL__ \
|
||||||
|
(Key &mappedKey, byte row, byte col, uint8_t keyState), __NL__ \
|
||||||
|
(mappedKey, row, col, keyState), ##__VA_ARGS__) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
/* Called before reporting our state to the host. This is the */ __NL__ \
|
||||||
|
/* last point in a cycle where a plugin can alter what gets */ __NL__ \
|
||||||
|
/* reported to the host. */ __NL__ \
|
||||||
|
OPERATION(beforeReportingState, __NL__ \
|
||||||
|
_NOT_ABORTABLE, __NL__ \
|
||||||
|
(),(),##__VA_ARGS__) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
/* Called at the very end of a cycle, after everything's */ __NL__ \
|
||||||
|
/* said and done. */ __NL__ \
|
||||||
|
OPERATION(afterEachCycle, __NL__ \
|
||||||
|
_NOT_ABORTABLE, __NL__ \
|
||||||
|
(),(),##__VA_ARGS__)
|
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
|
||||||
|
enum class EventHandlerResult {
|
||||||
|
OK,
|
||||||
|
EVENT_CONSUMED,
|
||||||
|
ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
#include "kaleidoscope/hooks.h"
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
|
||||||
|
// The following weak symbols are overwritten by
|
||||||
|
// using the KALEIDOSCOPE_INIT_PLUGINS(...) macro
|
||||||
|
// in the the firmware sketch. Their only purpose is
|
||||||
|
// to provide backwards compatibility during the transition
|
||||||
|
// from the old implementation of the hooking/plugin system
|
||||||
|
// to the new one. The weak symbols ensure that there
|
||||||
|
// are no undefined symbols if KALEIDOSCOPE_INIT_PLUGINS(...)
|
||||||
|
// is not used. This allows legacy sketches to be used
|
||||||
|
// during the transition phase.
|
||||||
|
|
||||||
|
#define INSTANTIATE_WEAK_HOOK_FUNCTION( \
|
||||||
|
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
__attribute__((weak)) __NL__ \
|
||||||
|
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \
|
||||||
|
return EventHandlerResult::OK; __NL__ \
|
||||||
|
}
|
||||||
|
|
||||||
|
_FOR_EACH_EVENT_HANDLER(INSTANTIATE_WEAK_HOOK_FUNCTION)
|
||||||
|
|
||||||
|
#undef INSTANTIATE_WEAK_HOOK_FUNCTION
|
||||||
|
|
||||||
|
} // namespace kaleidoscope
|
@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
union Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "event_handlers.h"
|
||||||
|
|
||||||
|
// Forward declaration required to enable friend declarations
|
||||||
|
// in class Hooks.
|
||||||
|
class kaleidoscope_;
|
||||||
|
extern void handleKeyswitchEvent(kaleidoscope::Key mappedKey, byte row, byte col, uint8_t keyState);
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
|
||||||
|
// The reason why the hook routing entry point functions live within
|
||||||
|
// class Hooks and not directly within a namespace is, that we want
|
||||||
|
// to restrict who is allowed to trigger hooks, mainly to prevent
|
||||||
|
// user code from calling hook methods.
|
||||||
|
//
|
||||||
|
// A note to maintainers: When you add new hooks that are supposed to
|
||||||
|
// be called from other places than the friend classes and functions listed
|
||||||
|
// below, just add a suitable friend declaration.
|
||||||
|
|
||||||
|
class Hooks {
|
||||||
|
|
||||||
|
// The following friend declarations restrict access to
|
||||||
|
// the hook routing system.
|
||||||
|
|
||||||
|
// Kaleidoscope_ calls Hooks::onSetup, Hooks::beforeReportingState
|
||||||
|
// and Hooks::afterEachCycle.
|
||||||
|
friend class Kaleidoscope_;
|
||||||
|
|
||||||
|
// ::handleKeyswitchEvent(...) calls Hooks::onKeyswitchEvent.
|
||||||
|
friend void ::handleKeyswitchEvent(kaleidoscope::Key mappedKey,
|
||||||
|
byte row, byte col, uint8_t keyState);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// The following private functions are just to be called by classes
|
||||||
|
// and functions that are declared as friends above.
|
||||||
|
|
||||||
|
#define DEFINE_WEAK_HOOK_FUNCTION( \
|
||||||
|
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
static EventHandlerResult HOOK_NAME SIGNATURE;
|
||||||
|
|
||||||
|
_FOR_EACH_EVENT_HANDLER(DEFINE_WEAK_HOOK_FUNCTION)
|
||||||
|
|
||||||
|
#undef DEFINE_WEAK_HOOK_FUNCTION
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* This file contains pre-processor macros, which are not namespaced, the
|
||||||
|
* vast majority of code these macros generate will live under the
|
||||||
|
* kaleidoscope_internal namespace. That is why this header is here.
|
||||||
|
*
|
||||||
|
* The only exception to this is the _KALEIDOSCOPE_INIT_PLUGINS macro, which
|
||||||
|
* also places a few symbols under the kaleidoscope namespace. But that code is
|
||||||
|
* tightly coupled with the other parts, which are in kaleidoscope_internal.
|
||||||
|
*
|
||||||
|
* The reason we use an entirely separate namespace is that
|
||||||
|
* Some plugins' classes are in the kaleidoscope namespace, and the instances
|
||||||
|
* of those classes have the same names as the classes, but in the global
|
||||||
|
* namespace. In these cases, the macros herein would resolve to the classes,
|
||||||
|
* when we want them to resolve to instances. To avoid this, we put the
|
||||||
|
* generated code in an entirely different namespace.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "macro_helpers.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
#include "kaleidoscope/hooks.h"
|
||||||
|
#include "eventhandler_signature_check.h"
|
||||||
|
#include "event_handlers.h"
|
||||||
|
|
||||||
|
// Some words about the design of hook routing:
|
||||||
|
//
|
||||||
|
// The EventDispatcher class implements a compile-time loop over all plugins, which
|
||||||
|
// calls an event handler on each plugin.
|
||||||
|
//
|
||||||
|
// Each hook called by the firmware core compiles down to only a single
|
||||||
|
// static function call (in class Hooks), rather than one for each plugin.
|
||||||
|
//
|
||||||
|
// This approach is better than using virtual event handlers in classes
|
||||||
|
// derived from kaleidoscope::Plugin because it results in significantly fewer
|
||||||
|
// virtual function calls and virtual function tables (vtables).
|
||||||
|
//
|
||||||
|
// The call to event handlers through kaleidoscope::Hooks and
|
||||||
|
// kaleidoscope_internal::EventDispatcher is templated to allow for compile time
|
||||||
|
// static polymorphisms (plugins' EventHandlers aren't virtual).
|
||||||
|
//
|
||||||
|
// Technically, plugins don't need to be derived from kaleidoscope::Plugin, but
|
||||||
|
// only need to implement a set of event handlers whose call signature match the
|
||||||
|
// API definition. This approach is similar to duck typing in scripting languages:
|
||||||
|
// The compiler only cares that a method's signature matches, not that the plugin
|
||||||
|
// is derived from kaleidoscope::Plugin.
|
||||||
|
//
|
||||||
|
// Static hook functions inside the Hooks class each use the EventDispatcher
|
||||||
|
// helper class to cast an associated EventHandler on each plugin instance.
|
||||||
|
|
||||||
|
|
||||||
|
// This defines an auxiliary class 'EventHandler_Foo' for each hook 'Foo'.
|
||||||
|
// Kaleidoscope::Hooks calls the EventDispatcher class, which in turn invokes
|
||||||
|
// the event handler method 'Foo' of each registered plugin with a given
|
||||||
|
// set of arguments.
|
||||||
|
|
||||||
|
#define _REGISTER_EVENT_HANDLER( \
|
||||||
|
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
namespace kaleidoscope_internal { __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
struct EventHandler_##HOOK_NAME { __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
static bool shouldAbortOnConsumedEvent() { __NL__ \
|
||||||
|
return SHOULD_ABORT_ON_CONSUMED_EVENT; __NL__ \
|
||||||
|
} __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
template<typename Plugin__, typename... Args__> __NL__ \
|
||||||
|
static kaleidoscope::EventHandlerResult __NL__ \
|
||||||
|
call(Plugin__ &plugin, Args__&&... hook_args) { __NL__ \
|
||||||
|
_VALIDATE_EVENT_HANDLER_SIGNATURE(HOOK_NAME, Plugin__) __NL__ \
|
||||||
|
return plugin.HOOK_NAME(hook_args...); __NL__ \
|
||||||
|
} __NL__ \
|
||||||
|
}; __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
} __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
namespace kaleidoscope { __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \
|
||||||
|
return kaleidoscope_internal::EventDispatcher::template __NL__ \
|
||||||
|
apply<kaleidoscope_internal::EventHandler_ ## HOOK_NAME> __NL__ \
|
||||||
|
ARGS_LIST; __NL__ \
|
||||||
|
} __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _INLINE_EVENT_HANDLER_FOR_PLUGIN(PLUGIN) \
|
||||||
|
__NL__ \
|
||||||
|
result = EventHandler__::call(PLUGIN, hook_args...); __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
if (EventHandler__::shouldAbortOnConsumedEvent() && __NL__ \
|
||||||
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) { __NL__ \
|
||||||
|
return result; __NL__ \
|
||||||
|
} __NL__
|
||||||
|
|
||||||
|
// _KALEIDOSCOPE_INIT_PLUGINS builds the loops that execute the plugins'
|
||||||
|
// implementations of the various event handlers.
|
||||||
|
//
|
||||||
|
// Its arguments are a list of references to plugin instances that have been
|
||||||
|
// instantiated in the global scope.
|
||||||
|
|
||||||
|
// EventDispatcher::apply() implements a compile time for-each loop over all
|
||||||
|
// plugins. The compiler automatically optimizes away calls to any plugin that
|
||||||
|
// doesn't implement an EventHandler for a given hook.
|
||||||
|
|
||||||
|
#define _KALEIDOSCOPE_INIT_PLUGINS(...) __NL__ \
|
||||||
|
namespace kaleidoscope_internal { __NL__ \
|
||||||
|
struct EventDispatcher { __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
/* Iterate through plugins, calling each one's event handler with */ __NL__ \
|
||||||
|
/* the arguments passed to the hook */ __NL__ \
|
||||||
|
template<typename EventHandler__, typename... Args__ > __NL__ \
|
||||||
|
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) { __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
kaleidoscope::EventHandlerResult result; __NL__ \
|
||||||
|
MAP(_INLINE_EVENT_HANDLER_FOR_PLUGIN, __VA_ARGS__) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
return result; __NL__ \
|
||||||
|
} __NL__ \
|
||||||
|
}; __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
} __NL__ \
|
||||||
|
/* We register event handlers here - which is not technically related */ __NL__ \
|
||||||
|
/* to initialization, nor is it in the same namespace - to support the */ __NL__ \
|
||||||
|
/* transition from the old APIs. When the user sketch does not use */ __NL__ \
|
||||||
|
/* KALEIDOSCOPE_INIT_PLUGINS(), the event handlers will not exist */ __NL__ \
|
||||||
|
/* either, thus wrapping them would produce a compile error. For this */ __NL__ \
|
||||||
|
/* reason, we do the wrapping here, tied to _KALEIDOSCOPE_INIT_PLUGINS. */ __NL__ \
|
||||||
|
/* */ __NL__ \
|
||||||
|
/* TODO(anyone): Move this somewhere else, outside of _internal, once */ __NL__ \
|
||||||
|
/* the V1 API is removed. */ __NL__ \
|
||||||
|
_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER)
|
@ -0,0 +1,124 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "macro_helpers.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
|
|
||||||
|
// *************************************************************************
|
||||||
|
// *************************************************************************
|
||||||
|
// NOTHING IN THIS HEADER WILL RESULT IN ANY CODE COMPILED INTO KALEIDOSCOPE
|
||||||
|
//
|
||||||
|
// YOU DO NOT NEED TO LOOK INSIDE THIS FILE IF YOU'RE TRYING TO UNDERSTAND
|
||||||
|
// HOW KALEIDOSCOPE WORKS.
|
||||||
|
// *************************************************************************
|
||||||
|
// *************************************************************************
|
||||||
|
|
||||||
|
// This header file implements compile-time method signature verification for
|
||||||
|
// plugins' event handlers as the _VALIDATE_EVENT_HANDLER_SIGNATURE macro.
|
||||||
|
|
||||||
|
// If the method signatures don't match the API definitions, compilation
|
||||||
|
// should fail with a helpful error message.
|
||||||
|
|
||||||
|
// *************************************************************************
|
||||||
|
// *************************************************************************
|
||||||
|
|
||||||
|
// We use non-virtual methods for event handlers. This reduces RAM (and to a
|
||||||
|
// smaller extent, PROGMEM) consumption, runtime call overhead, and allows the
|
||||||
|
// compiler to a better job removing dead code.
|
||||||
|
//
|
||||||
|
// The downside is that event handler methods in derived plugins are hidden,
|
||||||
|
// rather than overridden. Because only virtual methods can be overridden, we
|
||||||
|
// can't apply C++'s `override` keyword to hook methods.
|
||||||
|
//
|
||||||
|
// To make it easier to debug issues with plugins' event handler method
|
||||||
|
// signatures, the _VALIDATE_EVENT_HANDLER_SIGNATURE macro compares a plugin's
|
||||||
|
// event handler signatures with those of the kaleidoscope::Plugin baseclass. If
|
||||||
|
// any differences are detected, it outputs a compile-time error message.
|
||||||
|
//
|
||||||
|
// Ideally, we'd be able to use this (much simpler) code.
|
||||||
|
//
|
||||||
|
// decltype(&::kaleidoscope::Plugin::fooEventHandler)
|
||||||
|
// == decltype(&childPlugin::fooEventHandler)
|
||||||
|
//
|
||||||
|
// Unfortunately, this is not possible with C++-11, as it does not allow
|
||||||
|
// comparing function-types for equality. As a workaround, we can use the trait
|
||||||
|
// class EventHandlerSignaturesMatch to perform the comparison.
|
||||||
|
//
|
||||||
|
// It defines the bool constant 'value' as true if both signatures match.
|
||||||
|
// It checks the return value, argument types and const specification.
|
||||||
|
// (As of this writing, we don't have any const hook method.)
|
||||||
|
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
struct EventHandlerSignaturesMatch {
|
||||||
|
static constexpr bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// R: The return value,
|
||||||
|
// T1: Type of the first class (plugin),
|
||||||
|
// T2: Type of the second class (plugin),
|
||||||
|
// EventHandlerArgs: Variadic types of plugin event handler arguments.
|
||||||
|
|
||||||
|
template<typename R, typename T1, typename T2, typename... EventHandlerArgs>
|
||||||
|
struct EventHandlerSignaturesMatch<R(T1::*)(EventHandlerArgs...), R(T2::*)(EventHandlerArgs...)> {
|
||||||
|
static constexpr bool value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Equivalent to allow for const-eventhandlers, e.g. bool getFooEventHandler() const
|
||||||
|
template<typename R, typename T1, typename T2, typename... EventHandlerArgs> struct
|
||||||
|
EventHandlerSignaturesMatch<R(T1::*)(EventHandlerArgs...) const, R(T2::*)(EventHandlerArgs...) const> {
|
||||||
|
static constexpr bool value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This template is instantiated when something goes wrong.
|
||||||
|
// Because it does not define a constant 'value', it triggers a compiler error.
|
||||||
|
|
||||||
|
template<typename Plugin__, bool result> struct
|
||||||
|
___________Culprit_Plugin___________ { };
|
||||||
|
|
||||||
|
// This specialization is instantiated when everything is ok.
|
||||||
|
template<typename Plugin__> struct
|
||||||
|
___________Culprit_Plugin___________ <Plugin__, true> {
|
||||||
|
static constexpr bool value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the pointer types are the same, the signatures match, causing
|
||||||
|
// the first or second specialization to be instantiated. This makes
|
||||||
|
// the causes the compile time constant `value` to be defined as `true`.
|
||||||
|
// Otherwise, the unspecialized version of the template class is instantiated
|
||||||
|
// and `value` is defined as `false`
|
||||||
|
|
||||||
|
#define _VALIDATE_EVENT_HANDLER_SIGNATURE(EVENTHANDLER, PLUGIN) \
|
||||||
|
{ __NL__ \
|
||||||
|
/* Check if the signatures match. If not, the plugin has implemented */ __NL__ \
|
||||||
|
/* a method with a hook's name, but a different signature. */ __NL__ \
|
||||||
|
typedef EventHandlerSignaturesMatch< __NL__ \
|
||||||
|
decltype(&::kaleidoscope::Plugin::EVENTHANDLER), __NL__ \
|
||||||
|
decltype(&PLUGIN::EVENTHANDLER) __NL__ \
|
||||||
|
> Check; __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
static_assert(Check::value, __NL__ \
|
||||||
|
VERBOSE_STATIC_ASSERT_HEADER __NL__ \
|
||||||
|
"\nOne of your plugins implemented the \"" #EVENTHANDLER "\"" __NL__ \
|
||||||
|
"\nevent handler, but its signature didn't match the base class." __NL__ \
|
||||||
|
"\n" __NL__ \
|
||||||
|
"\nThe plugin with this issue will be marked in the compiler" __NL__ \
|
||||||
|
"\noutput with the string:" __NL__ \
|
||||||
|
"\n" __NL__ \
|
||||||
|
"\n ___________Culprit_Plugin___________." __NL__ \
|
||||||
|
"\n" __NL__ \
|
||||||
|
"\nYou should compare the event handlers implemented in this plugin" __NL__ \
|
||||||
|
"\nto those in \"kaleidoscope::Plugin\"." __NL__ \
|
||||||
|
"\n" __NL__ \
|
||||||
|
"\nAll of the event handler's argument types, return values and" __NL__ \
|
||||||
|
"\nconst qualifiers need to match." __NL__ \
|
||||||
|
"\n" __NL__ \
|
||||||
|
VERBOSE_STATIC_ASSERT_FOOTER __NL__ \
|
||||||
|
); __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
/* The following construct is necessary enable reporting of the */ __NL__ \
|
||||||
|
/* type of a plugin that implements an event handler with an */ __NL__ \
|
||||||
|
/* incorrect signature, because it's not possible to include any */ __NL__ \
|
||||||
|
/* non-literal string constant in a static_assert error message. */ __NL__ \
|
||||||
|
constexpr bool dummy = ___________Culprit_Plugin___________ __NL__ \
|
||||||
|
<PLUGIN, Check::value>::value; __NL__ \
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 William Swanson
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without
|
||||||
|
* restriction, including without limitation the rights to use, copy,
|
||||||
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
* of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||||
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* Except as contained in this notice, the names of the authors or
|
||||||
|
* their institutions shall not be used in advertising or otherwise to
|
||||||
|
* promote the sale, use or other dealings in this Software without
|
||||||
|
* prior written authorization from the authors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define EVAL0(...) __VA_ARGS__
|
||||||
|
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
|
||||||
|
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
|
||||||
|
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
|
||||||
|
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
|
||||||
|
#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
|
||||||
|
|
||||||
|
#define MAP_END(...)
|
||||||
|
#define MAP_OUT
|
||||||
|
#define MAP_COMMA ,
|
||||||
|
|
||||||
|
#define MAP_GET_END2() 0, MAP_END
|
||||||
|
#define MAP_GET_END1(...) MAP_GET_END2
|
||||||
|
#define MAP_GET_END(...) MAP_GET_END1
|
||||||
|
#define MAP_NEXT0(test, next, ...) next MAP_OUT
|
||||||
|
#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
|
||||||
|
#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next)
|
||||||
|
|
||||||
|
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
|
||||||
|
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
|
||||||
|
#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
|
||||||
|
|
||||||
|
#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
|
||||||
|
#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the function macro `f` to each of the remaining parameters.
|
||||||
|
*/
|
||||||
|
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the function macro `f` to each of the remaining parameters and
|
||||||
|
* inserts commas between the results.
|
||||||
|
*/
|
||||||
|
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
|
@ -0,0 +1,94 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kaleidoscope/event_handler_result.h"
|
||||||
|
#include "kaleidoscope_internal/event_dispatch.h"
|
||||||
|
#include "event_handlers.h"
|
||||||
|
|
||||||
|
#ifndef KALEIDOSCOPE_ENABLE_V1_PLUGIN_API
|
||||||
|
#define KALEIDOSCOPE_ENABLE_V1_PLUGIN_API 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
|
||||||
|
class Kaleidoscope_;
|
||||||
|
|
||||||
|
// A base class that implements default noop versions of all event
|
||||||
|
// handler methods.
|
||||||
|
//
|
||||||
|
// A note to developers:
|
||||||
|
//
|
||||||
|
// The only purpose of class EventHandlerBasePlugin
|
||||||
|
// is to enable the use of _FOR_EACH_EVENT_HANDLER
|
||||||
|
// to define default event handlers. This is currently not possible
|
||||||
|
// inside class Plugin directly as it would collide with
|
||||||
|
// the separate definition of onSetup(). This additional
|
||||||
|
// definition is, however, necessary to support the V1 plugin API.
|
||||||
|
//
|
||||||
|
// TODO(anyone): As soon as the V1 plugin API is removed from Kaleidoscope,
|
||||||
|
// class EventHandlerBasePlugin can be removed and its content be moved to
|
||||||
|
// class Plugin.
|
||||||
|
//
|
||||||
|
class EventHandlerBasePlugin {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
#define DEFINE_AND_IMPLEMENT_EVENT_HANDLER_METHOD( \
|
||||||
|
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
|
||||||
|
__NL__ \
|
||||||
|
EventHandlerResult HOOK_NAME SIGNATURE { __NL__ \
|
||||||
|
return EventHandlerResult::OK; __NL__ \
|
||||||
|
}
|
||||||
|
|
||||||
|
_FOR_EACH_EVENT_HANDLER(DEFINE_AND_IMPLEMENT_EVENT_HANDLER_METHOD)
|
||||||
|
|
||||||
|
#undef DEFINE_AND_IMPLEMENT_EVENT_HANDLER_METHOD
|
||||||
|
};
|
||||||
|
|
||||||
|
class Plugin : public EventHandlerBasePlugin {
|
||||||
|
|
||||||
|
friend class Kaleidoscope_;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API
|
||||||
|
/** Legacy plugin setup hook.
|
||||||
|
|
||||||
|
* In version one of the plugin API, `plugin->begin()` was called at
|
||||||
|
* `Kaleidoscope.use()` time, to perform boot-time initialization. We needed
|
||||||
|
* this because plugins are supposed to provide a singleton object, statically
|
||||||
|
* initialized at compile-time (with few exceptions). Because of this, the
|
||||||
|
* order in which they are instantiated is unspecified, and cannot be relied
|
||||||
|
* upon. For this reason, one's expected to explicitly initialize, "use" the
|
||||||
|
* plugins one wishes to, by calling `Kaleidoscope.use()` with a list of
|
||||||
|
* plugin object pointers.
|
||||||
|
*
|
||||||
|
* This is the only time this function will be called. It is intentionally
|
||||||
|
* protected, and accessible by the `Kaleidoscope` class only.
|
||||||
|
*
|
||||||
|
* Also, this method is deprecated in favour of the V2 API, which you can read
|
||||||
|
* about below.
|
||||||
|
*/
|
||||||
|
virtual void begin() {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Please see "event_handlers.h" for a list of supported event handlers and
|
||||||
|
// their documentation!
|
||||||
|
|
||||||
|
EventHandlerResult onSetup() {
|
||||||
|
#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API
|
||||||
|
// By letting the new `onSetup()` method call the legacy begin() method, we
|
||||||
|
// make sure that the old hooking interface is supported even if
|
||||||
|
// KALEIDOSCOPE_INIT_PLUGINS() is used to register a plugin that relies on
|
||||||
|
// the legacy `begin()` method to initialize itself and register hooks.
|
||||||
|
//
|
||||||
|
this->begin();
|
||||||
|
#endif
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace kaleidoscope
|
||||||
|
|
||||||
|
typedef kaleidoscope::Plugin KaleidoscopePlugin __attribute__((deprecated("class KaleidoscopePlugin is deprecated. Please derive plugins from kaleidoscope::Plugin instead.")));
|
Loading…
Reference in new issue