-
Notifications
You must be signed in to change notification settings - Fork 259
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
CharShift: Configuration plugin #1069
base: master
Are you sure you want to change the base?
Conversation
…bles Instead of using overridable functions, use function pointers (+ setters) to allow changing lookups and related things. This allows a bit more flexibility, and makes the user sketch simpler, because there is no override necessary there. Signed-off-by: Gergely Nagy <[email protected]>
Signed-off-by: Gergely Nagy <[email protected]>
CharShift::GetNumKeyPairsFunction CharShift::numKeyPairs_ = | ||
CharShift::numProgmemKeyPairs; |
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.
I feel like this is unnecessary, but I'm not sure I'm right. Couldn't the same num_keypairs_
variable be used, regardless of where the data is stored? As long as we're reading values from either PROGMEM or EEPROM, but not both (perhaps by using an index offset), there's no need to use three bytes to store a separate count, plus the function pointer, right? Or are you thinking the user might want to switch back and forth between the two without power-cycling the keyboard?
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.
I'm thinking that a user might want to use both at the same time, store some pairs in PROGMEM, some in EEPROM. Not sure if that makes sense for CharShift - it does for Macros and TapDance, where I copied the basic architecture from.
if (i < numKeyPairs()) { | ||
return readKeyPair(i); | ||
if (i < numKeyPairs_()) { | ||
return readKeyPair_(i); |
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.
Have we been using trailing underscores for private class function names as well as variables?
[My brain doesn't like the underscore immediately followed by the parens for some reason, but I'd much rather overhaul all the code to follow a consistent style than satisfy trivial preferences like that.]
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.
Honestly, I don't like it either. And no, we've been using trail-less private function names in the past. In this case, it's there because I first added the function pointers, then removed the original functions. It's safe and fine to rename them.
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.
I don't feel that I'm expert enough to do more than pose some (hopefully not stupid) questions. You mentioned that it would be impractical to use a function override in the config plugin (not the user sketch, which is pretty clearly undesirable) but I don't think I fully grasp why. Couldn't the EEPROM version store its max_pairs_
, and if that value is zero (or if the base EEPROM address is invalid), fall back on PROGMEM, thereby still functioning if nothing has been configured in EEPROM, without using function pointers?
Again, I'm not convinced that would be better, but it seems like it would be more economical in both PROGMEM and RAM, and perhaps there's some advantage to the function pointer approach that's not apparent to me.
I think if we'd use overrides, and did the override in the plugin, that would be susceptible to link-order issues. It would also make it that much harder for the end-user to have their override, would they wish to do so.
We already store
Not depending on linking order is one such advantage. Another - rather different - approach would be to derive |
Oooh, I had another idea! What if we had a single plugin, Thinking further... we'd still need I feel this would be lighter on PROGMEM and RAM usage than the current implementation, but would provide pretty much the same flexibility, and not be susceptible to link-order issues either. |
I can't imagine a use-case for a user code override of these functions. The only reason I used a weak definition in the first place was the idea that a CharShiftConfig plugin could override it. You think we'd still get link-order problems, even if only one of the functions has a weak definition? If so, how does it work to have a weak definition in a plugin, and an override in the sketch (possibly in a *.cpp file in the sketch's source tree, not just directly in the *.ino file itself)—wouldn't that have the same link-order problem, if there is one? Just trying to understand. |
I'm having trouble picturing this. By "derive", are you referring to inheritance? |
If I understand correctly, this would require only two extra bytes of RAM for the storage object pointer over the function override method that might suffer from link-order problems. I haven't been able to figure out how it will work, but I'd like to understand it. |
Yes.
Two bytes for the object pointer + whatever RAM the object itself takes. Which shouldn't be much more (if more at all) than what I'll push up a variant soon, to show what I had in mind. It seems like a cleaner architecture in this case anyway. |
Signed-off-by: Gergely Nagy <[email protected]>
Signed-off-by: Gergely Nagy <[email protected]>
Ok, did a quick comparison! Essentially the same sketch (0 builtin charshifts, 4 pairs in EEPROM):
As such, we're saving a lot of progmem, and some ram too. On the other hand, looking at the size map, we have a problem: in my implementation, I didn't use If we go down this route, we'd need to do it in a different way, and use templates, but that'd require the end-user to create the CharShift object, which is something we rarely do. Only our solid LED color plugin does that. So I don't think this is a good route. Pushed it up anyway to showcase it, but it doesn't work in its current form. Compiles, but will definitely not work. |
I wonder if we could cheat a bit, and use the props + typedef with static methods trickery we use for hardware devices and drivers... but that also sounds rather messy, and inflexible. |
So... that leaves us with another option: |
I really wish this wasn't true, almost as much as I wish Arduino didn't hide |
I'm definitely not a fan of that approach. I'm filled with dread every time I have reason to look at the hardware code—I invariably search in several wrong places before I find what I'm looking for, and it's nigh impossible to picture how the whole system fits together. |
I'm going to revisit this soon, in a large part because I ended up playing with a layout that could really benefit from CharShift, and I'd love to have Chrysalis support it too. There will be a few changes once I get around to update the plugin, based on lessons learned from DynamicMacros. First of all, I changed my stance on extend vs override. I originally said that I'd like to support in-flash & EEPROM pairs at the same time - I no longer think that makes sense. If we have EEPROM pairs, they should override the built-in ones. We could copy the built-in ones, though, assuming the slice is empty, maybe. |
This is a plugin on top of
CharShift
, similar toAutoShiftConfig
and inspired byDynamicTapDance
. Apart from a very quick compile, it is completely untested.The core idea is that we have two function pointers in
CharShift
that allows anyone to override thenumKeyPairs()
andreadKeyPair()
functions, but without relying on weak symbols, and theCharShiftConfig
uses this to extend the charshift map. The map is simply stored as an array of key pairs in EEPROM, allocation is to be done insetup()
, asCharShiftConfig.setup(0, 4)
(to allocate 4 keypairs, starting at index 0, thus, a complete override in this case).There's at least one thing missing: we need to be able to query the dynamic offset via Focus, so Chrysalis can present the editor in the future correctly.