1

I am using the QMK library, which has a LAYOUT macro that takes many parameters. It is used like so (with KC_xxx etc constants):

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[baselayer] = LAYOUT( /* Dvorak without modifiers. Never switched to, just as base for the combos*/
    KC_QUOT   ,KC_COMM   ,KC_DOT    ,KC_P      ,KC_Y     ,                      KC_F      ,KC_G      ,KC_C      ,KC_R      ,KC_L      ,
    KC_A      ,KC_O      ,KC_E      ,KC_U      ,KC_I     ,                      KC_D      ,KC_H      ,KC_T      ,KC_N      ,KC_S      ,
    KC_SCLN   ,KC_Q      ,KC_J      ,KC_K      ,KC_X     ,XXXXXXX   ,XXXXXXX   ,KC_B      ,KC_M      ,KC_W      ,KC_V      ,KC_Z      ,
    XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX  ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX
),
//... more layers
};

I'd like to split this code in an array and use that in the macro call:

// Define the array
const uint16_t BASE[] = {
    KC_QUOT   ,KC_COMM   ,KC_DOT    ,KC_P      ,KC_Y     ,                      KC_F      ,KC_G      ,KC_C      ,KC_R      ,KC_L      ,
    KC_A      ,KC_O      ,KC_E      ,KC_U      ,KC_I     ,                      KC_D      ,KC_H      ,KC_T      ,KC_N      ,KC_S      ,
    KC_SCLN   ,KC_Q      ,KC_J      ,KC_K      ,KC_X     ,XXXXXXX   ,XXXXXXX   ,KC_B      ,KC_M      ,KC_W      ,KC_V      ,KC_Z      ,
    XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX  ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX
};

// TODO: manipulate array

// Use the array
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[baselayer] = LAYOUT(BASE), // <-- how to write this line?
//... more layers
};

How do I change the indicated line of code so that the array BASE is unpacked and its elements used as the macro arguments?

Currently the compiler tells me

error: macro "LAYOUT" requires 44 arguments, but only 1 given

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ElRudi
  • 2,122
  • 2
  • 18
  • 33
  • 2
    I don't think you will be able to do this, because (as indicated by the error message) `LAYOUT` is a **macro** that will be used to *rewrite the code, at compile-time*, whereas any array you create will not actually have its value until runtime. – Karl Knechtel May 29 '22 at 21:07
  • What kind of syntax is `[baselayer] = `? – mkrieger1 May 29 '22 at 21:07
  • @mkrieger1, here is a link to the documentation: https://docs.qmk.fm/#/keymap?id=layers-and-keymaps – ElRudi May 29 '22 at 21:09
  • 1
    @mkrieger1 it's C syntax for designated initializers (it's not standard C++ but most compilers support it): `int arr[] = { [1] = 123, [4] = 567 };` would initialize elements 1 and 4 of the array – Jean-Michaël Celerier May 29 '22 at 21:11
  • right, yes, sorry - `baselayer` is the first value in an `enum` – ElRudi May 29 '22 at 21:12

1 Answers1

2

LAYOUT is a C preprocessor macro. You can't apply an array to a C preprocessor macro. It has to stay in the preprocessor world.

#define BASE \
    KC_QUOT   ,KC_COMM   ,KC_DOT    ,KC_P      ,KC_Y     ,                      KC_F      ,KC_G      ,KC_C      ,KC_R      ,KC_L      , \
    KC_A      ,KC_O      ,KC_E      ,KC_U      ,KC_I     ,                      KC_D      ,KC_H      ,KC_T      ,KC_N      ,KC_S      , \
    KC_SCLN   ,KC_Q      ,KC_J      ,KC_K      ,KC_X     ,XXXXXXX   ,XXXXXXX   ,KC_B      ,KC_M      ,KC_W      ,KC_V      ,KC_Z      , \
    XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX  ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX

#define EXPAND_THEN_LAYOUT(x)  LAYOUT(x)

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [baselayer] = EXPAND_THEN_LAYOUT(BASE),
};

I think I would do:

/// Dvorak without modifiers. Never switched to, just as base for the combos.
#define BASE_LAYOUT() LAYOUT( \
KC_QUOT   ,KC_COMM   ,KC_DOT    ,KC_P      ,KC_Y     ,                      KC_F      ,KC_G      ,KC_C      ,KC_R      ,KC_L      , \
KC_A      ,KC_O      ,KC_E      ,KC_U      ,KC_I     ,                      KC_D      ,KC_H      ,KC_T      ,KC_N      ,KC_S      , \
KC_SCLN   ,KC_Q      ,KC_J      ,KC_K      ,KC_X     ,XXXXXXX   ,XXXXXXX   ,KC_B      ,KC_M      ,KC_W      ,KC_V      ,KC_Z      , \
XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX  ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX   ,XXXXXXX \
)

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [baselayer] = BASE_LAYOUT(),
};

See What is the the best way to ask follow up questions?.

I want to insert a few values at certain index positions

Write a macro that replaces a certain position.

#define CHANGE_5_to_XXXX_IN(_1, _2, _3, _4, _5, ...) \
     _1, _2, _3, _4, XXXX, __VA_ARGS__
#define CHANGE_5_to_XXXX(...)  CHANGE_5_to_XXXX_IN(__VA_ARGS__)

EXPAND_THEN_LAYOUT(CHANGE_5_to_XXXX(BASE))
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Thanks @KamilCuk for that answer. Do I understand correctly, that `EXPAND_THEN_LAYOUT()` should then make the adjustments to `BASE`? What I need to do is e.g. insert values at certain index positions. If it's not too much work, could you show how I'd insert a `XXXXXXX` constant at e.g. positions 3, 4, and 5? – ElRudi May 29 '22 at 21:32
  • I do not understand. `EXPAND_THEN_LAYOUT` first expands `BASE` to a list of comma separated arguments, then applies the macro `LAYOUT` to these arguments, just like the original code did. I think this is what you mean by "adjustments to BASE". What do you mean by "index positions"? I am not familiar with qmk at all, I do not know what LAYOUT does or what it means. If you want change 3rd argument, change KC_DOT for XXXXXXX. – KamilCuk May 30 '22 at 07:08
  • Ah ok, I'll explain without using qmk. I have `#define`d the name `BASE` as a list of comma-seperated integers in `fileA.c` I then `#include` this in `fileB1.c` and insert those directly as arguments into the macro `LAYOUT`. This all I can do with your first snippet. So far, so good :) Now, in `fileB2.c`, I want to change the list, before using it in `LAYOUT`. E.g. I want to insert a few values at certain index positions. This too must be done at compile time. But I wouldn't know, how I'd achieve this - I cannot change it in `fileA`, because it should only affect `fileB2`, not `fileB1`. – ElRudi May 30 '22 at 10:44
  • 1
    ? So copy the definition to `fileB2` and modify it, this would be the simplest. This is preprocessor, not something smart. If you want something smart, do not use C preprocess, use python, or php, or perl, or jinja2, or anything else. See the edit. – KamilCuk May 30 '22 at 10:47
  • 1
    Yeah so simply copying the definition into `fileB2` is exactly what I'm trying to avoid. `fileA` has some "master data", and is changed relatively often. But I think the macro you added is exactly what I'm trying to do, I'll give it a try later. (I'll ask a separate question next time, thanks for the hint.) – ElRudi May 31 '22 at 10:18