New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plugin API redesign v2.1 #316
Conversation
doc/plugin-api-internals.md
Outdated
Which in turn becomes: | ||
|
||
```c++ | ||
static auto apply(Key &mappedKey, byte row, byte col, uint8_t keyState) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static auto apply(Key &mappedKey, byte row, byte col, uint8_t keyState) {
should be
static bool apply(Key &mappedKey, byte row, byte col, uint8_t keyState) {
to be valid C++11. About the actual return value, see the comment below.
doc/plugin-api-internals.md
Outdated
|
||
```c++ | ||
static auto apply(Key &mappedKey, byte row, byte col, uint8_t keyState) { | ||
ContinueIfHookReturnsTrue hook_return_val; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ContinueIfHookReturnsTrue
is actually a class, better replace it with bool
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most precise would be
decltype(ContinueIfHookReturnsTrue::eval(true)) hook_return_val;
but that's of no use here.
doc/plugin-api-internals.md
Outdated
we end up with: | ||
|
||
```c++ | ||
static auto apply(Key &mappedKey, byte row, byte col, uint8_t keyState) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
static auto apply(Key &mappedKey, byte row, byte col, uint8_t keyState) {
We talked with @obra at length last night, about naming things, and came up with the following:
|
Nice renaming. Wouldn't it be good to stick with the convention that class names are uppercase and go with |
2f2f106
to
ed62dc0
Compare
It would be, and we went with |
I think the code is now ready for review. I still have to clean up the history a little bit, but that shouldn't change the end result. |
14:57 <@obra> Looking at the amount of code we have for dealing with returntypes makes me really want to enforce the same return type on everything. I'm not saying that we Must do that, but it feels like we |
src/macro_helpers.h
Outdated
__NL__ ) | ||
( __NL__ \ | ||
[]{ /* Here we are in the body of a dummy lambda function. __NL__ \ | ||
[]{} is, BTW, the shortest way to write a lambda. __NL__ \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The __NL__
macro is meant to improve readability of pre-processed code. Adding it to lines that contain only comments will result in a bunch of empty lines in the resulting pre-processed code. I suppose that's unwanted. Because of this, the __NN__
macro was added. Its sole purpose is not to break the flow of newline macros in the original code and to improve its readability without generating undesired newlines for these lines after pre-processing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. My thought process was around the tradeoff between the generated code being more compact and the human-editable version of the code being more maintainable.
Ultimately, I expect that folks will be spending a lot more time with the 'real' implementation than the generated code. And some extra newlines in the generated code feel to me like they're not a significant hardship relative to making editing the code more complicated.
I've actually been wondering if astyle would do a good enough job with the generated code that we could instead just get rid of the NL macros entirely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And some extra newlines in the generated code feel to me like they're not a significant hardship relative to making editing the code more complicated.
Agreed.
I've actually been wondering if astyle would do a good enough job with the generated code that we could instead just get rid of the NL macros entirely.
As far as I get it, astyle is currently processing the original code. This won't fix the problems that arise with multi-line pre-processor macros. Every \
followed by a \n
is completely removed, and the whole macro ends up in a single, possibly very long line. That's why the __NL__
is useful.
Of course you could run astyle on the pre-processed code, it will probably add some line breaks. But I prefer the pre-processed code to have the same layout as the original code which aids orientation and code comparison.
Wow, I am impressed how fast you are progressing with this. May I add some remarks about the recent changes.
I like the idea of reducing the complexity by using a single return value type. However, your discussion suggests that you intent to add values that are possibly ignored. That sounds like a pretty bad idea, unless it is perfectly documented. As a programmer I always expect the calling instance to react reasonably on my return codes and I rely on that. When return codes are ignored, in general that means a design flaw or at least an obvious trap. That's why I would suggest to only define those values that are really required and acted upon. The Kaleidoscope coding style guide prefers enums to be named as constants. I would strongly recommend to use all-caps names for macros only. This makes it possible to rely on what is a symbol (no-strange side effects from replacement & type safety) and what is a macro (be prepared for any surprises). This differentiation, if taken serious, helps a lot when debugging. Is there any reason why you intent to use plain old enums instead of enum classes for the handlers' return values? Unless there are really good reasons against, the latter should be preferred. See here about the difference between both enum types. I read that you don't intent to call the plugins' dedicated callback methods 'handlers'. How do you intent to call them instead? In other contexts like GUI frameworks this is exactly what would be called event/signal handlers. The only difference here is that they are not dynamically registered. Nevertheless, the name seems appropriate to me. |
Seconded.
In the case of the event handlers, the name "handler" is certainly appropriate, but many of the hook functions don't actually "handle" anything. And in many cases, even the "event handler" hook functions might do something to respond to an event, but don't actually handle that event completely, so one could argue that "handler" is a bit of a misnomer even there. |
You are right, handlers aren't forced to handle. There are at least three names of this concept I am aware of: events&handlers, signals&slots and hooks. Maybe there are others. In any case, I think we should stick with conventions and use one of the established terms. |
__NL__ \ | ||
struct PluginMethod_##PLUGIN_METHOD { __NL__ \ | ||
__NL__ \ | ||
typedef SHOULD_ABORT_TYPE shouldAbort; __NL__ \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldAbort
might better be named uppercase as it is a type not an instance.
TODO list for myself:
|
In Kaleidoscope, we don't use the 'signals and slots' nomenclature for anything. |
I was refering to this part of your communication:
Which appears to be contradicted by your more recent post.
FWIW, I spend enough time with this piece of code that I am well aware that there are no signals&slots 😉 |
Yup, we're very much still feeling out the names. I was trying to figure out how the words you used mapped to what we have in Kaleidoscope and slightly misunderstood your comment as implying that we were using all those names. Right now, 'Handler' only actually gets used for key events. |
Yes, and I am very happy with most of the names you found. I really appreciate you taking so much time to name everything appropriately. Correct and telling names will help future developers a lot.
I see. So what do we call the other callbacks then. From my perspective, any of these methods have a signal character. Forget about the slots here (I never liked that name anyway). In many other projects those methods would probably be called signal handlers. To me it sounds a bit strange 'to handle a signal', but I am not a native speaker. Anyway, it is quite commonly used that way. The way you call the methods |
Just pushed a new update that changes the return type of all plugin methods to Next up: cleaning up the history, and updating all the plugins. |
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \ | ||
return kaleidoscope_internal::EventDispatcher::template __NL__ \ | ||
apply<kaleidoscope_internal::EventHandler_ ## HOOK_NAME> __NL__ \ | ||
(__VA_ARGS__); __NL__ \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace
(__VA_ARGS__)
with
ARGS_LIST
|
a088338
to
08d91c2
Compare
This is no longer WIP, and I force-pushed a squashed together branch. |
I know that you are eager to finish this and so am I. There is one tiny detail about the I added a leading After it has been moved to its own header |
I'd keep the leading underscore, if for nothing else, to signal that this is close to the internals, and should not be used lightly. |
Yeah. At least for now, this feels like "not part of the public API"
…On Fri, May 11, 2018 at 12:22 AM Gergely Nagy ***@***.***> wrote:
I'd keep the leading underscore, if for nothing else, to signal that this
is close to the internals, and should not be used lightly.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#316 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AACxaPkG9re6dZXI7BQeLTCXksDOyQ1Lks5txTwhgaJpZM4TyrGu>
.
|
src/plugin.h
Outdated
#endif | ||
|
||
public: | ||
// The following callbacks handle the synchronized communication |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This text needs to be updated as it is left over from when the event handlers were directly defined in Plugin
. I think we should refer the reader to src/event_handlers.h
where all the event handlers are defined and documented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The the terminology also needs to be updated according to the new glossary.
Also, I wouldn't write about callbacks here, as the term callback is mostly used for stand alone functions and not for class methods, even though they serve the same purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, thanks! Pushed an update that changes this text to a pointer to event_handlers.h
.
08d91c2
to
1f536f0
Compare
There's still the issue with the redundant Kaleidoscope instance which is, from my point of view, not urgent but at least worth an issue ticket after V2 has been merged. As far as I can see class |
1f536f0
to
36cd0aa
Compare
Whoops! Forgot about that. Fixed. Thanks again! |
src/Kaleidoscope.h
Outdated
// For compatibility reasons we enable the global variable Kaleidoscope | ||
// in global namespace. | ||
// | ||
extern kaleidoscope::Kaleidoscope_ &Kaleidoscope; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not urgent:
Damn! It just occured to me that we could have fixed this with a using declaration to generate a perfect alias and thereby saving 2 bytes of RAM.
using kaleidoscope::Kaleidoscope;
If someone changes this, don't forget to remove the instanciation of &Kaleidoscope
in kaleidoscope.cpp
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hah! Nice. It also appears to have saved us a few (20) bytes of PROGMEM too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
20 bytes! For a simple reference. I didn't expect that much.
36cd0aa
to
2bbcdb5
Compare
Back to being WIP:
We can support V1 & V2, but that means I need to update every plugin with wrappers. Mind you, that may end up being the better option... but a v2-only enforcement might still be useful nevertheless. |
Note to self, with regards to the 00:53 obra | algernon: something closer to the other use deprecation message, telling them how to do it. |
Also, @obra had a sweet idea: have an |
8b78505
to
020dceb
Compare
|
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>
023673d
to
8130dfd
Compare
Phew. Should be ready, again. |
+1 for the nice |
The text of that is largely @obra's work, all credit to him. And yes, it's <3 <3! |
|
||
Event handler | ||
|
||
: A function, usually provided by a `Plugin` that is run by a `Hook` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plugin is not well defined because it is used for the class Plugin
and also for Arduino libraries, that sometimes don't contain Plugin
classes. Might be a good candidate for the glossary.
+1 for the glossary and several +1s for all the effort to provide a smooth transition between APIs. |
This is a work in progress, building on top of #276.