diff --git a/src/macro_helpers.h b/src/macro_helpers.h new file mode 100644 index 00000000..31b0976e --- /dev/null +++ b/src/macro_helpers.h @@ -0,0 +1,123 @@ +#pragma once + +// Code generated by muli-line pre-processor macros is hard to read after +// pre-processing. +// +// When you want to check pre-processed code, e.g. for debugging or +// to understand what's going on, do the following: +// +// 1) Add the compiler command line definition +// -DKALEIDOSCOPE_ENABLE_MACRO_NEWLINE_SUBSTITUTION +// This prevents the __NL__ macro being defined below. +// 2) Generate the preprocessed code (it will contain a lot of __NL__ definitions). +// 3) Open the pre-processed code in your favorite editor. +// 3.1) Replace all __NL__ with newline characters. +// In vim the command would be ':%s/__NL__/\r/g'. +// 3.2) Autocorrect code-indenting to improve readability. This is necessary +// as pre-processor macros remove all whitespaces at the beginning of lines. +// With vim, the command gg=G helps (just type the characters one after +// the other). +// 4) Don't forget to remove the +// -DKALEIDOSCOPE_ENABLE_MACRO_NEWLINE_SUBSTITUTION +// from your compiler command line. Else the code won't compile. + +#ifndef KALEIDOSCOPE_ENABLE_MACRO_NEWLINE_SUBSTITUTION +#define __NL__ +#endif + +#define __NN__ + +// Some auxiliary macros +// +#define __STRINGIZE(S) #S +#define STRINGIZE(S) __STRINGIZE(S) + +// Allow for the creation of verbose messages in static_asserts +// +#define VERBOSE_STATIC_ASSERT_HEADER \ +__NL__ "\n" \ +__NL__ "\n***************************************************************" \ +__NL__ "\n******************** READ THIS CAREFULLY! *********************" \ +__NL__ "\n***************************************************************" \ +__NL__ "\n" + +#define VERBOSE_STATIC_ASSERT_FOOTER \ +__NL__ "\n" \ +__NL__ "\n***************************************************************" \ +__NL__ "\n***************************************************************" \ +__NL__ "\n***************************************************************" \ +__NL__ "\n" + +#define VERBOSE_FILE_INFO \ +__NL__ "\nFile: " __FILE__ + +#define VERBOSE_LINE_INFO \ +__NL__ "\nLine: " STRINGIZE(__LINE__) + +// The macro function RESTRICT_ARGS_COUNT can be used to generate more +// verbose error messages when users supply an insuitable number of arguments +// to a macro. +// +// For a macro it is used wherever one of the arguments A, B, C might +// be used, e.g. +// +#if 0 // This is just so that A_MACRO is not actually defined +#define A_MACRO(A, B, C, ...) \ + (void)RESTRICT_ARGS_COUNT(0, 3, A_MACRO, ##__VA_ARGS__); \ + int a = A; \ + int b = B; \ + int c = C; +#endif +// +// Note that RESTRICT_ARGS_COUNT can also be invoked wherever one of the macro +// arguments is used, e.g. +// +#if 0 // This is just so that B_MACRO is not actually defined +#define B_MACRO(A, B, C, ...) +int array[] = { A, B, RESTRICT_ARGS_COUNT(C, 3, B_MACRO, ##__VA_ARGS__) }; +#endif +// +#define RESTRICT_ARGS_COUNT(B, \ + NUM_EXPECTED_ARGS, \ + ORIGINAL_MACRO, \ + ...) \ +__NN__ ( \ +__NL__ []{ /* Here we are in the body of a dummy lambda function. \ +__NN__ []{} is, BTW, the shortest way to write a lambda. \ +__NN__ It is only used to hold the static_assert that cannot be \ +__NN__ defined directly in the keymap initializer list. By using the \ +__NN__ comma operator ((A, B) always evaluates to b), we ensure \ +__NN__ that not the lambda but B is what ASSERT_ARGS_COUNT \ +__NN__ finally evaluates to. \ +__NN__ Please not that passing B through this macro is a must \ +__NN__ as we need it for the comma operator to work. \ +__NN__ */ \ +__NN__ static_assert(sizeof(const char) == sizeof(#__VA_ARGS__ ), \ +__NN__ /* sizeof(const char) == sizeof(#__VA_ARGS__ ) checks the quoted \ +__NN__ list of additional arguments. If there are none, then the \ +__NN__ length of #__VA_ARGS__ is a single char as it contains '\0'. \ +__NN__ This check is not able to find the corner case of a single \ +__NN__ superfluous comma at the end of the macro arguments as this \ +__NN__ causes #__VA_ARGS__ being empty (only '\0'). \ +__NN__ */ \ +__NN__ VERBOSE_STATIC_ASSERT_HEADER \ +__NN__ \ +__NN__ VERBOSE_FILE_INFO \ +__NN__ VERBOSE_LINE_INFO \ +__NL__ "\n" \ +__NL__ "\nStrange arguments encountered in invocation of " #ORIGINAL_MACRO "." \ +__NL__ "\n" \ +__NL__ "\nPlease make sure to pass exactly " #NUM_EXPECTED_ARGS \ +__NN__ " macro arguments to" \ +__NL__ "\n" #ORIGINAL_MACRO ". Also make sure that there are no dangling" \ +__NL__ "\ncommas at the end of the argument list." \ +__NL__ "\n" \ +__NL__ "\nThis is the superfluous part at the end of the macro" \ +__NL__ "\narguments list: \'" #__VA_ARGS__ "\'" \ +__NN__ \ +__NN__ VERBOSE_STATIC_ASSERT_FOOTER \ +__NL__ ); \ +__NL__ \ +__NL__ }, /* End of dummy lambda, the comma operator's A operand. */ \ +__NL__ B /* The overall ASSERT_ARGS_COUNT evaluates to B. */ \ +__NL__ )