From 686044264e8815ffd1bf769e52e6ae6318753102 Mon Sep 17 00:00:00 2001 From: Florian Fleissner Date: Wed, 27 Nov 2019 11:36:43 +0100 Subject: [PATCH 1/3] Made the keymap a constexpr array This enables compile time exploration of the keymap from all code that is part of the sketch's compilation unit. Signed-off-by: Florian Fleissner --- src/kaleidoscope/layers.cpp | 2 +- src/kaleidoscope/layers.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kaleidoscope/layers.cpp b/src/kaleidoscope/layers.cpp index ac59b49b38..f3efb80e78 100644 --- a/src/kaleidoscope/layers.cpp +++ b/src/kaleidoscope/layers.cpp @@ -33,7 +33,7 @@ __attribute__((weak)) uint8_t layer_count = 0; __attribute__((weak)) -extern const Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] = {}; +extern constexpr Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] = {}; namespace kaleidoscope { uint32_t Layer_::layer_state_; diff --git a/src/kaleidoscope/layers.h b/src/kaleidoscope/layers.h index 0cf7d19636..aaa6762f8a 100644 --- a/src/kaleidoscope/layers.h +++ b/src/kaleidoscope/layers.h @@ -26,7 +26,7 @@ // file (*.ino) to define the keymap[] array that holds the user's // layers. It also computes the number of layers in that keymap. #define KEYMAPS(layers...) __NL__ \ - const Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] PROGMEM = { layers }; __NL__ \ + constexpr Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] PROGMEM = { layers }; __NL__ \ uint8_t layer_count __NL__ \ = sizeof(keymaps_linear) / sizeof(*keymaps_linear); __NL__ \ __NL__ \ From 7aa2c0e8596bd18412990067ebb5ee2ac6902f84 Mon Sep 17 00:00:00 2001 From: Florian Fleissner Date: Wed, 27 Nov 2019 11:45:53 +0100 Subject: [PATCH 2/3] Enabled templated hook methods Hook methods can now be templated. A template parameter type list, a list of template parameters and a list of dummy template arguments have been added to the macro arguments used in _FOR_EACH_EVENT_HANDLER. Non-template hooks pass empty parenthesis for the three newly introduced macro arguments. Signed-off-by: Florian Fleissner --- src/kaleidoscope/event_handlers.h | 36 ++++++++++++++ src/kaleidoscope/hooks.cpp | 5 +- src/kaleidoscope/hooks.h | 5 +- src/kaleidoscope/macro_helpers.h | 49 +++++++++++++++++++ src/kaleidoscope_internal/LEDModeManager.h | 4 +- src/kaleidoscope_internal/event_dispatch.h | 36 +++++++++----- .../eventhandler_signature_check.h | 11 +++-- .../type_traits/has_method.h | 11 +++-- 8 files changed, 136 insertions(+), 21 deletions(-) diff --git a/src/kaleidoscope/event_handlers.h b/src/kaleidoscope/event_handlers.h index 4bd8b38c33..b35985d78f 100644 --- a/src/kaleidoscope/event_handlers.h +++ b/src/kaleidoscope/event_handlers.h @@ -76,6 +76,25 @@ // kaleidoscope::EventHandlerResult::EVENT_CONSUMED. To enable this // pass the abortable flag value _ABORTABLE, _NOT_ABORTABLE otherwise. // +// template parameter type list: +// The hook's template type list in parenthesis, with a trailing comma. +// e.g. (, typename _T1, typename _T2) +// Pass empty parenthesis if the hook is non templatized. +// +// template parameters: +// The hook's template parameters in parenthesis, with a trailing comma. +// The template parameter names must match the template type list. +// e.g. (, _T1, _T2) +// Pass empty parenthesis if the hook is non templatized. +// +// dummy template arguments: +// Supply a list of already defined dummy types that could realistically +// be used to instantiate the template hook. Those types are only used +// during hook method signature checks. +// Please add parenthesis and a trailing comma. +// e.g. (, int, int) +// Pass empty parenthesis if the hook is non templatized. +// // call signature: // The type of arguments passed to the handlers as a comma separated // list in brackets. Every parameter must be named. @@ -93,12 +112,21 @@ " uint8_t keyState)\n" __NL__ \ "instead." +namespace kaleidoscope { + +// This dummy class can be used as dummy template argument to +// be passed in the definition of template hooks. +// +class SignatureCheckDummy {}; +} // namespace kaleidoscope + #define _FOR_EACH_EVENT_HANDLER(OPERATION, ...) __NL__ \ __NL__ \ OPERATION(onSetup, __NL__ \ 1, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \ _NOT_ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (),(), ##__VA_ARGS__) __NL__ \ __NL__ \ /* Called at the very start of each cycle, before gathering */ __NL__ \ @@ -107,6 +135,7 @@ 1, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \ _NOT_ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (), (), ##__VA_ARGS__) __NL__ \ __NL__ \ /* DEPRECATED */ __NL__ \ @@ -121,6 +150,7 @@ 1, __NL__ \ DEPRECATED(ON_KEYSWITCH_EVENT_HANDLER_V1), __NL__ \ _ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (Key &mappedKey, byte row, byte col, uint8_t keyState), __NL__ \ (mappedKey, row, col, keyState), ##__VA_ARGS__) __NL__ \ __NL__ \ @@ -135,6 +165,7 @@ 2, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \ _ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (Key &mappedKey, KeyAddr key_addr, uint8_t keyState), __NL__ \ (mappedKey, key_addr, keyState), ##__VA_ARGS__) __NL__ \ __NL__ \ @@ -150,6 +181,7 @@ 1, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \ _ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (const char *command), __NL__ \ (command), ##__VA_ARGS__) __NL__ \ __NL__ \ @@ -160,6 +192,7 @@ 1, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \ _NOT_ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (), (), ##__VA_ARGS__) __NL__ \ /* Called when the LED mode changes. If one needs to know what */ __NL__ \ /* from and what to the mode changed, they should track that */ __NL__ \ @@ -168,6 +201,7 @@ 1, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \ _NOT_ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (), (), ##__VA_ARGS__) __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__ \ @@ -176,6 +210,7 @@ 1, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \ _NOT_ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (),(),##__VA_ARGS__) __NL__ \ __NL__ \ /* Called at the very end of a cycle, after everything's */ __NL__ \ @@ -184,6 +219,7 @@ 1, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \ _NOT_ABORTABLE, __NL__ \ + (),(),(), /* non template */ __NL__ \ (),(),##__VA_ARGS__) // The following function macro lists event handler/hook method names and diff --git a/src/kaleidoscope/hooks.cpp b/src/kaleidoscope/hooks.cpp index ac25c44cec..9ad71c5075 100644 --- a/src/kaleidoscope/hooks.cpp +++ b/src/kaleidoscope/hooks.cpp @@ -31,8 +31,11 @@ namespace kaleidoscope { #define INSTANTIATE_WEAK_HOOK_FUNCTION( \ HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \ - SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \ + SHOULD_ABORT_ON_CONSUMED_EVENT, \ + TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, TMPL_DUMMY_ARGS_LIST, \ + SIGNATURE, ARGS_LIST) __NL__ \ __NL__ \ + MAKE_TEMPLATE_SIGNATURE(UNWRAP TMPL_PARAM_TYPE_LIST) __NL__ \ __attribute__((weak)) __NL__ \ EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \ return EventHandlerResult::OK; __NL__ \ diff --git a/src/kaleidoscope/hooks.h b/src/kaleidoscope/hooks.h index abc6d5f1b4..012196ccec 100644 --- a/src/kaleidoscope/hooks.h +++ b/src/kaleidoscope/hooks.h @@ -72,8 +72,11 @@ class Hooks { #define DEFINE_WEAK_HOOK_FUNCTION( \ HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \ - SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \ + SHOULD_ABORT_ON_CONSUMED_EVENT, \ + TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, TMPL_DUMMY_ARGS_LIST, \ + SIGNATURE, ARGS_LIST) __NL__ \ __NL__ \ + MAKE_TEMPLATE_SIGNATURE(UNWRAP TMPL_PARAM_TYPE_LIST) __NL__ \ static EventHandlerResult HOOK_NAME SIGNATURE; _FOR_EACH_EVENT_HANDLER(DEFINE_WEAK_HOOK_FUNCTION) diff --git a/src/kaleidoscope/macro_helpers.h b/src/kaleidoscope/macro_helpers.h index c056bf25a8..eeb50a244c 100644 --- a/src/kaleidoscope/macro_helpers.h +++ b/src/kaleidoscope/macro_helpers.h @@ -52,6 +52,8 @@ #define GLUE3(A, B, C) A##B##C #define GLUE4(A, B, C, D) A##B##C##D +#define UNWRAP(...) __VA_ARGS__ + // Allow for the creation of verbose messages in static_asserts // #define VERBOSE_STATIC_ASSERT_HEADER \ @@ -142,3 +144,50 @@ int array[] = { A, B, RESTRICT_ARGS_COUNT(C, 3, B_MACRO, ##__VA_ARGS__) }; /* Count the args in a list */ #define NUM_ARGS(...) (sizeof((int[])__VA_ARGS__)/sizeof(int)) + +// Macro MAKE_TEMPLATE_SIGNATURE(...) wraps arguments 2..last in a template +// signature like 'template' only if it is passed more +// than one argument. Otherwise it generates an empty string. +// +#define MAKE_TEMPLATE_SIGNATURE(UNUSED, ...) \ + SELECT_ON_EMPTY_SIGNATURE(MAKE_TEMPLATE_SIGNATURE_, UNUSED,##__VA_ARGS__) +#define MAKE_TEMPLATE_SIGNATURE_0(...) +#define MAKE_TEMPLATE_SIGNATURE_1(...) template<__VA_ARGS__> + +// Macro ADD_TEMPLATE_BRACES(...) wraps arguments 2..last in angle brackets +// only if it is passed more than one argument. Otherwise it generates an +// empty string. +// +#define ADD_TEMPLATE_BRACES(UNUSED, ...) \ + SELECT_ON_EMPTY_SIGNATURE(ADD_TEMPLATE_BRACES_, UNUSED,##__VA_ARGS__) +#define ADD_TEMPLATE_BRACES_0(...) +#define ADD_TEMPLATE_BRACES_1(...) <__VA_ARGS__> + +// Macro TEMPLATE_KEYWORD(...) generates the 'template' keyword only if it is +// passed more than one argument. +// +#define TEMPLATE_KEYWORD(UNUSED, ...) \ + SELECT_ON_EMPTY_SIGNATURE(TEMPLATE_KEYWORD_, UNUSED,##__VA_ARGS__) +#define TEMPLATE_KEYWORD_0(...) +#define TEMPLATE_KEYWORD_1(...) template + +// Helper macros for template parameter list treatment + +#define GLUE2_AUX(...) GLUE2(__VA_ARGS__) + +#define TEST1(UNUSED, A, B, C, D, \ + E, F, G, H, \ + I, J, K, L, \ + M, N, O, P, \ + Q, ...) Q +#define TEST(...) TEST1(__VA_ARGS__) +#define CHOICE(UNUSED, ...) ,##__VA_ARGS__, 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 0 +#define SELECT_ON_EMPTY_SIGNATURE(MACRO_BASE_NAME, UNUSED, ...) \ + GLUE2_AUX( \ + MACRO_BASE_NAME, \ + TEST(CHOICE(1,##__VA_ARGS__)) \ + )(__VA_ARGS__) diff --git a/src/kaleidoscope_internal/LEDModeManager.h b/src/kaleidoscope_internal/LEDModeManager.h index 2c93fccd35..a1277f7875 100644 --- a/src/kaleidoscope_internal/LEDModeManager.h +++ b/src/kaleidoscope_internal/LEDModeManager.h @@ -82,7 +82,9 @@ inline void registerLEDModeActivated(Bool2Type, // Noop } -DEFINE_HAS_METHOD_TRAITS(Plugin, registerLEDModeActivated, void, (uint8_t led_mode_id)) +DEFINE_HAS_METHOD_TRAITS(Plugin, + /* registerLEDModeActivated not templated */ (), (), + registerLEDModeActivated, void, (uint8_t led_mode_id)) // A templated implementation of LEDModeFactoryFunc. // diff --git a/src/kaleidoscope_internal/event_dispatch.h b/src/kaleidoscope_internal/event_dispatch.h index a93a024fcd..4186c6ae2b 100644 --- a/src/kaleidoscope_internal/event_dispatch.h +++ b/src/kaleidoscope_internal/event_dispatch.h @@ -38,6 +38,7 @@ #include "kaleidoscope/hooks.h" #include "kaleidoscope_internal/eventhandler_signature_check.h" #include "kaleidoscope/event_handlers.h" +#include "kaleidoscope_internal/keymap_exploration.h" // Some words about the design of hook routing: // @@ -79,40 +80,48 @@ #define _REGISTER_EVENT_HANDLER( \ HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \ - SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) \ + SHOULD_ABORT_ON_CONSUMED_EVENT, \ + TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, TMPL_DUMMY_ARGS_LIST, \ + SIGNATURE, ARGS_LIST) __NL__ \ __NL__ \ namespace kaleidoscope_internal { __NL__ \ __NL__ \ template __NL__ \ + typename Plugin__ __NL__ \ + UNWRAP TMPL_PARAM_TYPE_LIST /* may evaluate empty */ __NL__ \ + , typename... Args__> __NL__ \ struct _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) { __NL__ \ static DEPRECATION_TAG kaleidoscope::EventHandlerResult __NL__ \ call(Plugin__ &plugin, Args__&&... hook_args) { __NL__ \ - return plugin.HOOK_NAME(hook_args...); __NL__ \ + return plugin.TEMPLATE_KEYWORD(UNWRAP TMPL_PARAM_LIST) HOOK_NAME __NL__ \ + ADD_TEMPLATE_BRACES(UNWRAP TMPL_PARAM_LIST) __NL__ \ + (hook_args...); __NL__ \ } __NL__ \ }; __NL__ \ __NL__ \ /* This specialization is used for those hooks that a plugin does not __NL__ \ * implement. __NL__ \ */ __NL__ \ - template __NL__ \ + template __NL__ \ struct _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) __NL__ \ - { __NL__ \ + { __NL__ \ static kaleidoscope::EventHandlerResult __NL__ \ call(Plugin__ &/*plugin*/, Args__&&... /*hook_args*/) { __NL__ \ return kaleidoscope::EventHandlerResult::OK; __NL__ \ } __NL__ \ }; __NL__ \ __NL__ \ + MAKE_TEMPLATE_SIGNATURE(UNWRAP TMPL_PARAM_TYPE_LIST) __NL__ \ struct _NAME4(EventHandler_, HOOK_NAME, _v, HOOK_VERSION) { __NL__ \ __NL__ \ static bool shouldAbortOnConsumedEvent() { __NL__ \ return SHOULD_ABORT_ON_CONSUMED_EVENT; __NL__ \ } __NL__ \ __NL__ \ - template __NL__ \ + template __NL__ \ static kaleidoscope::EventHandlerResult __NL__ \ call(Plugin__ &plugin, Args__&&... hook_args) { __NL__ \ __NL__ \ @@ -129,8 +138,9 @@ typedef _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) __NL__ \ < __NL__ \ derived_implements_hook, __NL__ \ - Plugin__, __NL__ \ - Args__... __NL__ \ + Plugin__ __NL__ \ + UNWRAP TMPL_PARAM_LIST __NL__ \ + , Args__... __NL__ \ > Caller; __NL__ \ __NL__ \ return Caller::call(plugin, hook_args...); __NL__ \ @@ -141,11 +151,13 @@ __NL__ \ namespace kaleidoscope { __NL__ \ __NL__ \ + MAKE_TEMPLATE_SIGNATURE(UNWRAP TMPL_PARAM_TYPE_LIST) __NL__ \ EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \ return kaleidoscope_internal::EventDispatcher::template __NL__ \ apply __NL__ \ - ARGS_LIST; __NL__ \ + ::_NAME4(EventHandler_, HOOK_NAME, _v, HOOK_VERSION) __NL__ \ + ADD_TEMPLATE_BRACES(UNWRAP TMPL_PARAM_LIST) __NL__ \ + >ARGS_LIST; __NL__ \ } __NL__ \ __NL__ \ } diff --git a/src/kaleidoscope_internal/eventhandler_signature_check.h b/src/kaleidoscope_internal/eventhandler_signature_check.h index 97ac9c891b..5fdf3c31ef 100644 --- a/src/kaleidoscope_internal/eventhandler_signature_check.h +++ b/src/kaleidoscope_internal/eventhandler_signature_check.h @@ -92,13 +92,17 @@ template struct #define _DEFINE_IMPLEMENTATION_CHECK_CLASS_SPECIALIZATION( \ HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \ - SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) \ + SHOULD_ABORT_ON_CONSUMED_EVENT, \ + TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, TMPL_DUMMY_ARGS_LIST, \ + SIGNATURE, ARGS_LIST) \ \ /* We use the generalized traits class found in header has_method.h __NL__ \ * to do check if a plugin defines a hook method with a specific __NL__ \ * signature. __NL__ \ */ __NL__ \ - DEFINE_HAS_METHOD_TRAITS(GLUE2(Plugin, HOOK_VERSION), HOOK_NAME, __NL__ \ + DEFINE_HAS_METHOD_TRAITS(GLUE2(Plugin, HOOK_VERSION), __NL__ \ + TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, __NL__ \ + HOOK_NAME, __NL__ \ kaleidoscope::EventHandlerResult, __NL__ \ SIGNATURE) __NL__ \ __NL__ \ @@ -107,7 +111,8 @@ template struct */ __NL__ \ template __NL__ \ struct HookVersionImplemented_##HOOK_NAME __NL__ \ - : public GLUE4(Plugin, HOOK_VERSION, _HasMethod_, HOOK_NAME) __NL__ \ + : public GLUE4(Plugin, HOOK_VERSION, _HasMethod_, HOOK_NAME) __NL__ \ + __NL__ \ {}; #define _PREPARE_EVENT_HANDLER_SIGNATURE_CHECK_START(HOOK_NAME, ...) \ diff --git a/src/kaleidoscope_internal/type_traits/has_method.h b/src/kaleidoscope_internal/type_traits/has_method.h index 3051deca09..11431ced21 100644 --- a/src/kaleidoscope_internal/type_traits/has_method.h +++ b/src/kaleidoscope_internal/type_traits/has_method.h @@ -18,13 +18,15 @@ #include "kaleidoscope/macro_helpers.h" -#define DEFINE_HAS_METHOD_TRAITS(PREFIX, METHOD_NAME, \ +#define DEFINE_HAS_METHOD_TRAITS(PREFIX, \ + TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, \ + METHOD_NAME, \ RETURN_TYPE, ARGUMENTS) \ __NL__ \ /* This traits checks if a class of type Class__ __NL__ \ * implements a method with given signature. __NL__ \ */ __NL__ \ - template __NL__ \ + template __NL__ \ struct GLUE3(PREFIX, _HasMethod_, METHOD_NAME) __NL__ \ { __NL__ \ /* Define a pointer to member function with the correct __NL__ \ @@ -55,7 +57,10 @@ * (SFINAE = substitution failure is not an error) __NL__ \ * and the test(...) overload is used instead. __NL__ \ */ __NL__ \ - static_cast(&ClassAux__::METHOD_NAME), bool{} __NL__ \ + static_cast( __NL__ \ + &ClassAux__::TEMPLATE_KEYWORD(UNWRAP TMPL_PARAM_LIST) __NL__ \ + METHOD_NAME ADD_TEMPLATE_BRACES(UNWRAP TMPL_PARAM_LIST) __NL__ \ + ), bool{} __NL__ \ ) __NL__ \ test(int /* unused */) __NL__ \ { __NL__ \ From a538328ba2f702595abb3b203868942467cf83b5 Mon Sep 17 00:00:00 2001 From: Florian Fleissner Date: Thu, 28 Nov 2019 18:42:15 +0100 Subject: [PATCH 3/3] Added sketch exploration This commit adds a new template hook exploreSketch() that allows plugins to efficiently obtain compile-time known information about the sketch. The hook is called before setup and is passed a _Sketch template parameter that wraps two more types Plugins and StaticKeymap, whose static constexpr methods can be used to explore the registered plugins and the static keymap. Signed-off-by: Florian Fleissner --- examples/Internal/Sketch_Exploration/Makefile | 55 ++++ .../Sketch_Exploration/Sketch_Exploration.ino | 139 ++++++++++ src/kaleidoscope/Kaleidoscope.cpp | 1 + src/kaleidoscope/Kaleidoscope.h | 1 + src/kaleidoscope/event_handlers.h | 20 +- src/kaleidoscope/hooks.cpp | 14 + src/kaleidoscope/hooks.h | 5 + src/kaleidoscope/layers.h | 5 +- src/kaleidoscope_internal/event_dispatch.h | 6 +- .../sketch_exploration/keymap_exploration.h | 256 ++++++++++++++++++ .../sketch_exploration/plugin_exploration.h | 185 +++++++++++++ .../sketch_exploration/sketch_exploration.h | 71 +++++ 12 files changed, 753 insertions(+), 5 deletions(-) create mode 100644 examples/Internal/Sketch_Exploration/Makefile create mode 100644 examples/Internal/Sketch_Exploration/Sketch_Exploration.ino create mode 100644 src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h create mode 100644 src/kaleidoscope_internal/sketch_exploration/plugin_exploration.h create mode 100644 src/kaleidoscope_internal/sketch_exploration/sketch_exploration.h diff --git a/examples/Internal/Sketch_Exploration/Makefile b/examples/Internal/Sketch_Exploration/Makefile new file mode 100644 index 0000000000..996bde9889 --- /dev/null +++ b/examples/Internal/Sketch_Exploration/Makefile @@ -0,0 +1,55 @@ +# This stub makefile for a Kaleidoscope example pulls in all the targets +# required to build the example + +UNAME_S := $(shell uname -s) + +ifeq ($(UNAME_S),Darwin) +SKETCHBOOK_DIR ?= $(HOME)/Documents/Arduino +PACKAGE_DIR ?= $(HOME)/Library/Arduino15 +else +SKETCHBOOK_DIR ?= $(HOME)/Arduino +PACKAGE_DIR ?= $(HOME)/.arduino15 +endif + + +ARDUINO_INSTALLED_ENV=$(shell ls -dt $(PACKAGE_DIR)/packages/keyboardio/hardware/avr 2>/dev/null |head -n 1) +MANUALLY_INSTALLED_ENV=$(shell ls -dt $(SKETCHBOOK_DIR)/hardware/keyboardio/avr 2>/dev/null |head -n 1) + + + +ifneq ("$(wildcard $(ARDUINO_INSTALLED_ENV)/boards.txt)","") + +ifneq ("$(wildcard $(MANUALLY_INSTALLED_ENV)/boards.txt)","") + +$(info ***************************************************************************) +$(info It appears that you have installed two copies of Kaleidoscope. One copy was) +$(info installed using Arduino's "Board Manager", while the other was installed by) +$(info hand, probably using "git".) +$(info ) +$(info This will likely cause some trouble as you try to build keyboard firmware) +$(info using Kaleidoscope. You may want to remove either: ) +$(info ) +$(info $(PACKAGE_DIR)/packages/keyboardio/ which was installed using Arduino) +$(info ) +$(info or) +$(info ) +$(info $(SKETCHBOOK_DIR)/hardware/keyboardio/ which was installed by hand.) +$(info ) +$(info ***************************************************************************) +$(info ) + +endif + +BOARD_HARDWARE_PATH = $(ARDUINO_INSTALLED_ENV) +KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR ?= build-tools/makefiles/ +KALEIDOSCOPE_BUILDER_DIR ?= $(ARDUINO_INSTALLED_ENV)/libraries/Kaleidoscope/bin/ + + + +endif + + +BOARD_HARDWARE_PATH ?= $(SKETCHBOOK_DIR)/hardware +KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR ?= keyboardio/avr/build-tools/makefiles/ + +include $(BOARD_HARDWARE_PATH)/$(KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR)/rules.mk diff --git a/examples/Internal/Sketch_Exploration/Sketch_Exploration.ino b/examples/Internal/Sketch_Exploration/Sketch_Exploration.ino new file mode 100644 index 0000000000..443a9cd76e --- /dev/null +++ b/examples/Internal/Sketch_Exploration/Sketch_Exploration.ino @@ -0,0 +1,139 @@ +/* -*- mode: c++ -*- + * Basic -- A very basic Kaleidoscope example + * Copyright (C) 2018 Keyboard.io, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "Kaleidoscope.h" + + +// This example demonstrates how a plugin can gather information about +// the keymap at compile time, e.g. to adapt its behavior, safe resources, ... + +/* *INDENT-OFF* */ +KEYMAPS( + [0] = KEYMAP_STACKED + ( + Key_NoKey, Key_1, Key_1, Key_1, Key_4, Key_5, Key_NoKey, + Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, + Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, + + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + Key_NoKey, + + Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, + Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, + Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, + Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + + Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, + Key_NoKey + ), +) +/* *INDENT-ON* */ + +using namespace kaleidoscope::sketch_exploration; + +class BPlugin : public kaleidoscope::Plugin {}; +class CPlugin : public kaleidoscope::Plugin {}; + +// A simple plugin that defines just one hook. +// +class APlugin : public kaleidoscope::Plugin { + + public: + APlugin() : has_key_1_{false} {} + + template + kaleidoscope::EventHandlerResult exploreSketch() { + + // Static keymap exploration + + typedef typename _Sketch::StaticKeymap K; + + // Important: Always make sure to call _Sketch::StaticKeymap's methods + // in a constexpr context. This is done by + // passing their value to a constexpr temporary variable. + + constexpr uint8_t n_key_1 = K::collect(NumKeysEqual{Key_1}); + static_assert(n_key_1 == 3, "Error determining key count"); + + constexpr bool has_key_1 = K::collect(HasKey{Key_1}); + static_assert(has_key_1, "Error querying key existence"); + has_key_1_ = has_key_1; // Assign the temporary that was computed + // at compile time. + + constexpr Key max_key = K::collect(MaxKeyRaw{}); + static_assert(max_key.getRaw() > 0, ""); + + static_assert(K::getKey(0 /*layer*/, KeyAddr{2, 3}) == Key_D, + "Key lookup failed"); + + constexpr auto n_layers = K::nLayers(); + constexpr auto layer_size = K::layerSize(); + + // Plugin exploration + // + // Use macros ENTRY_TYPE, ENRTY_IS_LAST, PLUGIN_POSITION, + // PLUGIN_IS_REGISTERED and NUM_OCCURRENCES to retreive information + // about the plugins that are registered in the sketch. + + typedef typename _Sketch::Plugins P; + + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + + static_assert(P::size == 3, ""); + + static_assert(!ENRTY_IS_LAST(P, 0), ""); + static_assert(!ENRTY_IS_LAST(P, 1), ""); + static_assert(ENRTY_IS_LAST(P, 2), ""); + + static_assert(PLUGIN_POSITION(P, APlugin) == 0, ""); + static_assert(PLUGIN_POSITION(P, BPlugin) == 1, ""); + static_assert(PLUGIN_POSITION(P, CPlugin) == -1, ""); + + static_assert(PLUGIN_IS_REGISTERED(P, APlugin) == true, ""); + static_assert(PLUGIN_IS_REGISTERED(P, BPlugin) == true, ""); + static_assert(PLUGIN_IS_REGISTERED(P, CPlugin) == false, ""); + + static_assert(NUM_OCCURRENCES(P, APlugin) == 2, ""); + static_assert(NUM_OCCURRENCES(P, BPlugin) == 1, ""); + static_assert(NUM_OCCURRENCES(P, CPlugin) == 0, ""); + + return kaleidoscope::EventHandlerResult::OK; + } + + private: + + bool has_key_1_; +}; + +APlugin a_plugin1, a_plugin2; +BPlugin b_plugin; + +KALEIDOSCOPE_INIT_PLUGINS( + a_plugin1, + b_plugin, + a_plugin2 +) + +void setup() { + Kaleidoscope.setup(); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/src/kaleidoscope/Kaleidoscope.cpp b/src/kaleidoscope/Kaleidoscope.cpp index 02a7aaafd1..2ce2dfca22 100644 --- a/src/kaleidoscope/Kaleidoscope.cpp +++ b/src/kaleidoscope/Kaleidoscope.cpp @@ -36,6 +36,7 @@ Kaleidoscope_::setup(void) { // properly. device().serialPort().begin(9600); + kaleidoscope::sketch_exploration::pluginsExploreSketch(); kaleidoscope::Hooks::onSetup(); device().setup(); diff --git a/src/kaleidoscope/Kaleidoscope.h b/src/kaleidoscope/Kaleidoscope.h index b588cdd5a0..9b7dbe53dd 100644 --- a/src/kaleidoscope/Kaleidoscope.h +++ b/src/kaleidoscope/Kaleidoscope.h @@ -59,6 +59,7 @@ static constexpr DEPRECATED(LED_COUNT) uint8_t LED_COUNT = kaleidoscope_internal #include "kaleidoscope/key_events.h" #include "kaleidoscope/hid.h" #include "kaleidoscope/layers.h" +#include "kaleidoscope_internal/sketch_exploration/sketch_exploration.h" #include "kaleidoscope/macro_map.h" #include "kaleidoscope_internal/event_dispatch.h" #include "kaleidoscope_internal/LEDModeManager.h" diff --git a/src/kaleidoscope/event_handlers.h b/src/kaleidoscope/event_handlers.h index b35985d78f..6b13f4de2f 100644 --- a/src/kaleidoscope/event_handlers.h +++ b/src/kaleidoscope/event_handlers.h @@ -220,7 +220,19 @@ class SignatureCheckDummy {}; _CURRENT_IMPLEMENTATION, __NL__ \ _NOT_ABORTABLE, __NL__ \ (),(),(), /* non template */ __NL__ \ - (),(),##__VA_ARGS__) + (),(),##__VA_ARGS__) __NL__ \ + __NL__ \ + /* Called before setup to enable plugins at compile time */ __NL__ \ + /* to explore the sketch. */ __NL__ \ + OPERATION(exploreSketch , __NL__ \ + 1, __NL__ \ + _CURRENT_IMPLEMENTATION, __NL__ \ + _NOT_ABORTABLE, __NL__ \ + (,typename _Sketch), __NL__ \ + (,_Sketch), __NL__ \ + (,kaleidoscope::SignatureCheckDummy), __NL__ \ + (), __NL__ \ + (),##__VA_ARGS__) // The following function macro lists event handler/hook method names and // available versions. It is used to auto-generate code that is @@ -273,4 +285,8 @@ class SignatureCheckDummy {}; __NL__ \ START(afterEachCycle, 1) __NL__ \ OP(afterEachCycle, 1) __NL__ \ - END(afterEachCycle, 1) + END(afterEachCycle, 1) __NL__ \ + __NL__ \ + START(exploreSketch, 1) __NL__ \ + OP(exploreSketch, 1) __NL__ \ + END(exploreSketch, 1) diff --git a/src/kaleidoscope/hooks.cpp b/src/kaleidoscope/hooks.cpp index 9ad71c5075..929aa8e8ca 100644 --- a/src/kaleidoscope/hooks.cpp +++ b/src/kaleidoscope/hooks.cpp @@ -45,4 +45,18 @@ _FOR_EACH_EVENT_HANDLER(INSTANTIATE_WEAK_HOOK_FUNCTION) #undef INSTANTIATE_WEAK_HOOK_FUNCTION +namespace sketch_exploration { +class Sketch; +} + +// Make sure that there is no undefined symbol if KALEIDOSCOPE_INIT_PLUGINS(...) +// is not invoked in the user's sketch. +// +template<> +__attribute__((weak)) +EventHandlerResult Hooks::exploreSketch +() { + return EventHandlerResult::OK; +}; + } // namespace kaleidoscope diff --git a/src/kaleidoscope/hooks.h b/src/kaleidoscope/hooks.h index 012196ccec..5ea358c108 100644 --- a/src/kaleidoscope/hooks.h +++ b/src/kaleidoscope/hooks.h @@ -41,6 +41,10 @@ class LEDControl; // Forward declaration to enable friend declarations. class Layer_; +namespace sketch_exploration { +void pluginsExploreSketch(); +} // namespace sketch_exploration + // 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 @@ -60,6 +64,7 @@ class Hooks { friend class Kaleidoscope_; friend class ::kaleidoscope::Layer_; friend class ::kaleidoscope::plugin::LEDControl; + friend void ::kaleidoscope::sketch_exploration::pluginsExploreSketch(); // ::handleKeyswitchEvent(...) calls Hooks::onKeyswitchEvent. friend void ::handleKeyswitchEvent(kaleidoscope::Key mappedKey, diff --git a/src/kaleidoscope/layers.h b/src/kaleidoscope/layers.h index aaa6762f8a..c4e258bc45 100644 --- a/src/kaleidoscope/layers.h +++ b/src/kaleidoscope/layers.h @@ -21,6 +21,7 @@ #include "kaleidoscope/keymaps.h" #include "kaleidoscope/device/device.h" #include "kaleidoscope_internal/device.h" +#include "kaleidoscope_internal/sketch_exploration/sketch_exploration.h" // Macro for defining the keymap. This should be used in the sketch // file (*.ino) to define the keymap[] array that holds the user's @@ -33,7 +34,9 @@ /* This deprecated compatibility wrapper is removed by the linker if __NL__ \ it is not accessed nowhere. __NL__ \ */ __NL__ \ - kaleidoscope::internal::Keymaps2DInterface keymaps; + kaleidoscope::internal::Keymaps2DInterface keymaps; __NL__ \ + __NL__ \ + _INIT_SKETCH_EXPLORATION extern uint8_t layer_count; diff --git a/src/kaleidoscope_internal/event_dispatch.h b/src/kaleidoscope_internal/event_dispatch.h index 4186c6ae2b..be51e7aa40 100644 --- a/src/kaleidoscope_internal/event_dispatch.h +++ b/src/kaleidoscope_internal/event_dispatch.h @@ -38,7 +38,7 @@ #include "kaleidoscope/hooks.h" #include "kaleidoscope_internal/eventhandler_signature_check.h" #include "kaleidoscope/event_handlers.h" -#include "kaleidoscope_internal/keymap_exploration.h" +#include "kaleidoscope_internal/sketch_exploration/sketch_exploration.h" // Some words about the design of hook routing: // @@ -214,4 +214,6 @@ __NL__ \ /* This generates a PROGMEM array-kind-of data structure that contains */ __NL__ \ /* LEDModeFactory entries */ __NL__ \ - _INIT_LED_MODE_MANAGER(__VA_ARGS__) + _INIT_LED_MODE_MANAGER(__VA_ARGS__) __NL__ \ + __NL__ \ + _INIT_PLUGIN_EXPLORATION(__VA_ARGS__) diff --git a/src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h b/src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h new file mode 100644 index 0000000000..2f3ef23bfd --- /dev/null +++ b/src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h @@ -0,0 +1,256 @@ +/* Kaleidoscope - Firmware for computer input devices + * Copyright (C) 2013-2019 Keyboard.io, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "kaleidoscope/key_defs.h" + +namespace kaleidoscope { +namespace sketch_exploration { + +// A simple keymap adaptor class that makes the keymap conveniently accessible. +// at compiletime. +// +template +class KeymapAdaptor { + private: + + const Key(&keymap_)[_n_layers][_layer_size]; + + public: + + static constexpr uint8_t n_layers = _n_layers; + static constexpr uint8_t layer_size = _layer_size; + + constexpr KeymapAdaptor(const Key(&keymap)[_n_layers][_layer_size]) + : keymap_{keymap} + {} + + constexpr Key getKey(uint8_t layer, uint8_t offset) { + return keymap_[layer][offset]; + } + constexpr Key getKey(uint8_t layer, KeyAddr key_addr) { + return this->getKey(layer, key_addr.toInt()); + } +}; + +// This class implements compile time keymap traversal. +// +// Every key is visited an the _Accumulation functor decides on how +// the key's value affects the overal return value (accumulation). +// +template +class AccumulationHelper : public KeymapAdaptor<_n_layers, _layer_size> { + private: + + const _Accumulation &op_; + + private: + + typedef typename _Accumulation::ResultType ResultType; + + constexpr ResultType accumulateOnLayer(uint8_t layer, uint8_t offset) { + return (offset >= _layer_size) + ? op_.init_value + : op_.apply(this->getKey(layer, offset), + this->accumulateOnLayer(layer, offset + 1)); + } + + constexpr ResultType accumulate(uint8_t layer) { + return (layer >= _n_layers) + ? op_.init_value + : op_.apply(this->accumulateOnLayer(layer, 0), + this->accumulate(layer + 1)); + } + + public: + + typedef KeymapAdaptor<_n_layers, _layer_size> ParentType; + + constexpr AccumulationHelper(const Key(&keymap)[_n_layers][_layer_size], + const _Accumulation &op) + : ParentType{keymap}, + op_{op} + {} + + constexpr ResultType apply() { + return this->accumulate(0); + } +}; + +// Accumulation functors to be used with the KeymapInterface's collect +// method. +// +// Plugins can implement their own functors to request other types of +// information + +struct MaxKeyRaw { + typedef Key ResultType; + static constexpr ResultType init_value = 0; + + constexpr ResultType apply(Key k1, Key k2) { + return (k1 > k2) ? k1 : k2; + } +}; + +struct NumKeysEqual { + typedef uint8_t ResultType; + static constexpr ResultType init_value = 0; + + constexpr NumKeysEqual(Key k) : k_{k} {} + + constexpr ResultType apply(Key test_key, ResultType r) { + return (test_key == k_) ? r + 1 : r; + } + constexpr ResultType apply(ResultType r1, ResultType r2) { + return r1 + r2; + } + + Key k_; +}; + +struct HasKey { + typedef bool ResultType; + static constexpr ResultType init_value = false; + + constexpr HasKey(Key k) : k_{k} {} + + constexpr ResultType apply(Key test_key, ResultType r) { + return (test_key == k_) ? true : r; + } + constexpr ResultType apply(ResultType r1, ResultType r2) { + return r1 || r2; + } + + Key k_; +}; + +// This class is actually defined and implemented in _INIT_KEYMAP_EXPLORATION +// which is invoked by KALEIDOSCOPE_INIT_PLUGINS +// +class KeymapInterface; + +extern void pluginsExploreSketch(); + +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Read carefully +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// About compile-time-only functions (COMPILE_TIME_USE_ONLY) +// +// On Harvard architecture an array like the Kaleidoscope keymap +// that is declared 'constexpr PROGMEM' must either be accessed +// by a runtime function through functions like pgm_read_byte +// or at compile time with ordinary array access. Using normal array +// access at run-time will cause a memory access failures as the MCU +// will try to read from SRAM instead of PROGMEM. +// +// There are some functions that are therefore restricted to compile-time +// use and will fail if instantiated by the compiler. There is no language +// inherent means to prevent the latter. Therefore, we flag all such functions +// as +// +// COMPILE_TIME_USE_ONLY +// +// Call such functions consistently as +// +// constexpr auto val = aCompileTimeOnlyFunction(...); +// +// This ensures that they can not be instantiated and called at runtime. +// +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +// This macro defines a Sketch interface class that is passed to the +// exploreSketch<_Sketch>(...)-hook. +// +#define _INIT_KEYMAP_EXPLORATION \ + namespace kaleidoscope { \ + namespace sketch_exploration { \ + class StaticKeymap \ + { \ + private: \ + \ + template \ + static constexpr auto accumulationHelper( \ + const Key (&keymap)[_n_layers][_layer_size], \ + const _Accumulation &op) \ + -> AccumulationHelper<_n_layers, _layer_size, _Accumulation> \ + { \ + return AccumulationHelper<_n_layers, _layer_size, \ + _Accumulation>{keymap, op}; \ + } \ + \ + template \ + static constexpr auto accumulationHelper(const _Accumulation &op) \ + -> decltype(accumulationHelper(::keymaps_linear, op)) \ + { \ + return accumulationHelper(::keymaps_linear, op); \ + } \ + \ + template \ + static constexpr auto keymapAdaptor( \ + const Key (&keymap)[_n_layers][_layer_size]) \ + -> KeymapAdaptor<_n_layers, _layer_size> \ + { \ + return KeymapAdaptor<_n_layers, _layer_size>{keymap}; \ + } \ + \ + static constexpr auto keymapAdaptor() \ + -> decltype(keymapAdaptor(::keymaps_linear)) \ + { \ + return keymapAdaptor(::keymaps_linear); \ + } \ + \ + public: \ + \ + /* COMPILE_TIME_USE_ONLY (see explanation above) \ + * \ + * The collect function visits all keys in the keymap and generates \ + * a result based on the requirements that are defined by the \ + * _Accumulation functor op. \ + * \ + * This approach is necessary as there is currently (c++11) no \ + * language feature available that would enable compile time iterative \ + * traversal of arrays like the keymap. \ + * \ + * Examples for an accumulation could e.g. be the number of occurences \ + * of a specific key or if a key is present at all... \ + */ \ + template \ + static constexpr auto collect(const _Accumulation &op) \ + -> typename _Accumulation::ResultType \ + { \ + return accumulationHelper(op).apply(); \ + } \ + \ + /* COMPILE_TIME_USE_ONLY (see explanation above) \ + */ \ + static constexpr Key getKey(uint8_t layer, KeyAddr key_addr) { \ + return keymapAdaptor().getKey(layer, key_addr); \ + } \ + \ + static constexpr uint8_t nLayers() { \ + return keymapAdaptor().n_layers; \ + } \ + static constexpr uint8_t layerSize() { \ + return keymapAdaptor().layer_size; \ + } \ + }; \ + } /* namespace sketch_exploration */ \ + } /* namespace kaleidoscope */ + +} // namespace sketch_exploration +} // namespace kaleidoscope diff --git a/src/kaleidoscope_internal/sketch_exploration/plugin_exploration.h b/src/kaleidoscope_internal/sketch_exploration/plugin_exploration.h new file mode 100644 index 0000000000..afb20bb6b2 --- /dev/null +++ b/src/kaleidoscope_internal/sketch_exploration/plugin_exploration.h @@ -0,0 +1,185 @@ +/* Kaleidoscope - Firmware for computer input devices + * Copyright (C) 2013-2019 Keyboard.io, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "kaleidoscope_internal/type_traits/type_traits" + +namespace kaleidoscope { +namespace sketch_exploration { + +struct EmptyPluginTypeList { + typedef void Plugin; + typedef EmptyPluginTypeList Next; + static constexpr int size = 0; + static constexpr bool is_last = true; +}; + +template +struct BareType { + typedef typename std::remove_const < + typename std::remove_reference<_T>::type + >::type Type; +}; + +template +struct PluginTypeList { + typedef typename BareType<_Plugin>::Type Plugin; + + static constexpr int id = _id; + + typedef PluginTypeList < _id + 1, _MorePlugins... > Next; + + static constexpr int size = 1 + Next::size; + static constexpr bool is_last = false; +}; + +template +struct PluginTypeList<_id, _Plugin> { + typedef typename BareType<_Plugin>::Type Plugin; + + static constexpr int id = _id; + + typedef EmptyPluginTypeList Next; + + static constexpr int size = 1; + static constexpr bool is_last = true; +}; + +template +auto makePluginTypeList(const _Plugins&...p) -> PluginTypeList<0, _Plugins...> {} + +template +struct Entry { + typedef typename Entry < _PluginTypeList, _id - 1 >::Next::Plugin Type; + typedef typename Entry < _PluginTypeList, _id - 1 >::Next::Next Next; + + static constexpr int id = Entry < _PluginTypeList, _id - 1 >::id + 1; + static_assert(id == _id, ""); + + static constexpr bool is_last = Entry < _PluginTypeList, _id - 1 >::Next::is_last; +}; + +template +struct Entry<_PluginTypeList, 0> { + typedef typename _PluginTypeList::Plugin Type; + typedef typename _PluginTypeList::Next Next; + + static constexpr int id = 0; + static constexpr bool is_last = _PluginTypeList::is_last; +}; + +template +struct OccurrencesAux { + static constexpr int value = + (std::is_same::Type, _WantedPlugin>::value) + ? OccurrencesAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value + 1 + : OccurrencesAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value; +}; + +template +struct OccurrencesAux<_PluginTypeList, _WantedPlugin, 0> { + static constexpr int value + = (std::is_same::Type, _WantedPlugin>::value) + ? 1 + : 0; +}; + +template +struct Occurrences + : public OccurrencesAux < _PluginTypeList, _WantedPlugin, _PluginTypeList::size - 1 > {}; + +template +struct PluginPositionAux { + static constexpr int value = + (PluginPositionAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value != -1) + ? PluginPositionAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value + : (std::is_same::Type, _WantedPlugin>::value) + ? _id + : -1; +}; + +template +struct PluginPositionAux<_PluginTypeList, _WantedPlugin, 0> { + static constexpr int value + = (std::is_same::Type, _WantedPlugin>::value) + ? 0 + : -1; +}; + +template +struct PluginPosition + : public PluginPositionAux < _PluginTypeList, _WantedPlugin, _PluginTypeList::size - 1 > {}; + +template +struct IsRegistered { + static constexpr bool value + = (PluginPosition<_PluginTypeList, _WantedPlugin>::value != -1); +}; + +template +struct Plugins__ { + + static constexpr int size = _PluginTypeList::size; + + // C++11 does not allow for template specialization to havven + // in non-namespace scope. Thus, we define those templates + // in namespace scope and then using declare their types here. + // + template + using Entry = Entry<_PluginTypeList, _id>; + + template + using Position = PluginPosition<_PluginTypeList, _PluginType>; + + template + using IsRegistered = IsRegistered<_PluginTypeList, _PluginType>; + + template + using Occurrences = Occurrences<_PluginTypeList, _PluginType>; +}; + +} // namespace sketch_exploration +} // namespace kaleidoscope + +#define _FIX_PLUGIN_TYPE_AND_NAME_AMBIGUITIES(T) T + +#define _INIT_PLUGIN_EXPLORATION(...) \ + \ + /* We have to define this type list in global namespace as some plugins \ + * exhibit ambiguities between their type name and the name of \ + * their global instance :( \ + */ \ + typedef decltype( \ + kaleidoscope::sketch_exploration::makePluginTypeList( __VA_ARGS__) \ + ) Kaleidoscope_SketchExploration__ConcretePluginTypeList; \ + \ + namespace kaleidoscope { \ + namespace sketch_exploration { \ + class Plugins : public Plugins__< \ + Kaleidoscope_SketchExploration__ConcretePluginTypeList> {}; \ + } /* namespace sketch_exploration */ \ + } /* namespace kaleidoscope */ + +// Convenience macro that deal with the ugly typedef and typename stuff... +// +#define ENTRY_TYPE(PLUGINS, ID) \ + typename PLUGINS::template Entry::Type + +#define ENRTY_IS_LAST(PLUGINS, ID) PLUGINS::template Entry::is_last +#define PLUGIN_POSITION(PLUGINS, TYPE) PLUGINS::template Position::value +#define PLUGIN_IS_REGISTERED(PLUGINS, TYPE) PLUGINS::template IsRegistered::value +#define NUM_OCCURRENCES(PLUGINS, TYPE) PLUGINS::template Occurrences::value diff --git a/src/kaleidoscope_internal/sketch_exploration/sketch_exploration.h b/src/kaleidoscope_internal/sketch_exploration/sketch_exploration.h new file mode 100644 index 0000000000..9fd9b5adbe --- /dev/null +++ b/src/kaleidoscope_internal/sketch_exploration/sketch_exploration.h @@ -0,0 +1,71 @@ +/* Kaleidoscope - Firmware for computer input devices + * Copyright (C) 2013-2019 Keyboard.io, Inc. + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "kaleidoscope_internal/sketch_exploration/keymap_exploration.h" +#include "kaleidoscope_internal/sketch_exploration/plugin_exploration.h" + +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Read carefully +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// About compile-time-only functions (COMPILE_TIME_USE_ONLY) +// +// On Harvard architecture an array like the Kaleidoscope keymap +// that is declared 'constexpr PROGMEM' must either be accessed +// by a runtime function through functions like pgm_read_byte +// or at compile time with ordinary array access. Using normal array +// access at run-time will cause a memory access failures as the MCU +// will try to read from SRAM instead of PROGMEM. +// +// There are some functions that are therefore restricted to compile-time +// use and will fail if instantiated by the compiler. There is no language +// inherent means to prevent the latter. Therefore, we flag all such functions +// as +// +// COMPILE_TIME_USE_ONLY +// +// Call such functions consistently as +// +// constexpr auto val = aCompileTimeOnlyFunction(...); +// +// This ensures that they can not be instantiated and called at runtime. +// +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +// This macro defines a Sketch interface class that is passed to the +// exploreSketch<_Sketch>(...)-hook. +// +#define _INIT_SKETCH_EXPLORATION \ + \ + _INIT_KEYMAP_EXPLORATION \ + \ + namespace kaleidoscope { \ + namespace sketch_exploration { \ + \ + class Plugins; \ + \ + struct Sketch { \ + typedef sketch_exploration::StaticKeymap StaticKeymap; \ + typedef sketch_exploration::Plugins Plugins; \ + }; \ + \ + void pluginsExploreSketch() { \ + Hooks::exploreSketch(); \ + } \ + } /* namespace sketch_exploration */ \ + } /* namespace kaleidoscope */