diff --git a/examples/Internal/Sketch_Exploration/Makefile b/examples/Internal/Sketch_Exploration/Makefile
new file mode 100644
index 00000000..996bde98
--- /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 00000000..443a9cd7
--- /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 02a7aaaf..2ce2dfca 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 b588cdd5..9b7dbe53 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 4bd8b38c..6b13f4de 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,7 +219,20 @@
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_NOT_ABORTABLE, __NL__ \
- (),(),##__VA_ARGS__)
+ (),(),(), /* non template */ __NL__ \
+ (),(),##__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
@@ -237,4 +285,8 @@
__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 ac25c44c..929aa8e8 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__ \
@@ -42,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 abc6d5f1..5ea358c1 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,
@@ -72,8 +77,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/layers.cpp b/src/kaleidoscope/layers.cpp
index ac59b49b..f3efb80e 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 0cf7d196..c4e258bc 100644
--- a/src/kaleidoscope/layers.h
+++ b/src/kaleidoscope/layers.h
@@ -21,19 +21,22 @@
#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
// 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__ \
/* 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/macro_helpers.h b/src/kaleidoscope/macro_helpers.h
index c056bf25..eeb50a24 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 2c93fccd..a1277f78 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 a93a024f..be51e7aa 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/sketch_exploration/sketch_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__ \
}
@@ -202,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/eventhandler_signature_check.h b/src/kaleidoscope_internal/eventhandler_signature_check.h
index 97ac9c89..5fdf3c31 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/sketch_exploration/keymap_exploration.h b/src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h
new file mode 100644
index 00000000..2f3ef23b
--- /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 00000000..afb20bb6
--- /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 00000000..9fd9b5ad
--- /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 */
diff --git a/src/kaleidoscope_internal/type_traits/has_method.h b/src/kaleidoscope_internal/type_traits/has_method.h
index 3051deca..11431ced 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__ \