Help making a macro table - I get error: initializer fails to determine size of '__c'

So I want to make a macro layer for strings with a function that takes each macro name and returns the string, like this:

enum { MACRO_VERSION_INFO,
       MACRO_ANY,

   M_Prog, M_1, M_2, M_3, M_4, 
   M_5, M_LEDEffectNext,
   M_Backtick, M_Q, 
   M_W, 
   Ma_E, 
   M_R, 
   M_T, 
   M_Tab,
   M_PageUp,   M_A, M_S, M_D, M_F, M_G,
   M_PageDown, M_Z, M_X, M_C, M_V, M_B, M_Escape,
   M_LeftControl, M_Backspace, M_LeftGui, M_LeftShift,
   M_LeftFunction,

   M_Any,  M_6, M_7, M_8,     M_9,         M_0,         M_Numpad,
   M_Enter,      M_Y, M_U, M_I,     M_O,         M_P,         M_Equals,
                  M_H, M_J, M_K,     M_L,         M_Semicolon, M_Quote,
   M_RightAlt,  M_N, M_M, M_Comma, M_Period,    M_Slash,     M_Minus,
   M_RightShift, M_LeftAlt, M_Spacebar, M_RightControl,
   M_RightFunction
       
     };

char* words(uint8_t macroIndex) {
    switch (macroIndex) {
      case M_N: return "Hello";
      return "Hi";
    }
}

const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
  switch (macroIndex) {

  case MACRO_VERSION_INFO:
    versionInfoMacro(keyState);
    break;

  case MACRO_ANY:
    anyKeyMacro(keyState);
    break;


   case M_Prog: case M_1: case M_2: 
   case M_3: case M_4: case M_5: 
   case M_LEDEffectNext: 
   case M_Backtick: case M_Q: case M_W: case Ma_E: 
   case M_R: case M_T: case M_Tab: 
   case M_PageUp: case   M_A: case M_S: case M_D: 
   case M_F: case M_G: case M_PageDown: 
   case M_Z: case M_X: case M_C: case M_V: 
   case M_B: case M_Escape: case M_LeftControl: 
   case M_Backspace: case M_LeftGui: case M_LeftShift: 
   case M_LeftFunction: 
   
   case M_Any: case M_6: case M_7: case M_8: 
   case M_9: case M_0: case M_Numpad: 
   case M_Enter: case M_Y: case M_U: case M_I: 
   case M_O: case M_P: case M_Equals: 
   case M_H: case M_J: case M_K: case M_L: 
   case M_Semicolon: case M_Quote: case M_RightAlt: 
   case M_N: case M_M: case M_Comma: case M_Period: 
   case M_Slash: case M_Minus: case M_RightShift: 
   case M_LeftAlt: case M_Spacebar: case M_RightControl:
   case M_RightFunction: 
   if (keyToggledOn(keyState)) {
    return Macros.type(PSTR(words(macroIndex)));
   }
 
   break;
  }
  return MACRO_NONE;
}

But I get this error:

In function 'const macro_t* macroAction(uint8_t, uint8_t)':

311: error: initializer fails to determine size of '__c'

 return Macros.type(PSTR(words(macroIndex)));

                    ^
311: error: array must be initialized with a brace-enclosed initializer

Why am I getting this error?

The argument of PSTR must be a compile-time constant. It can’t be the return value of a function.

1 Like

Ok, thanks.

So can I use a constant array and index them that way? Or is there a better way to do the lookup?

Going forward, the best might be to expose Macro.lookupAsciiCode, and then you could do something like this:

const char *text = word(macroIndex);
for (uint8_t i = 0; text[i]; i++) {
  Key key = Macro.lookupAsciiCode(text[i]);
  kaleidoscope::hid::pressKey(key);
  kaleidoscope::hid::sendReport();
  kaleidoscope::hid::releaseKey(key);
}

This needs the .lookupAsciiCode method made public, though, and will use considerably more RAM (of which we only have 2k of). Alternatively, words could return progmem-stored strings, but that makes the code a little more complicated.

Or, you can just Macro.type(PSTR("blah")) in the case branches directly. If you use the same string twice, put it in a variable:

static const char *blah_text = PSTR("blah");

switch (macroIndex) {
  case M_SOMETHING:
  case M_ANOTHER:
    Macro.type(blah_text);
    break;
  case M_THIRD:
   Macro.type(PSTR("Third time's the charm!"));
   break;
}

This last one is probably the best.

A fourth option would be to have a variant of Macro.type() that accepts a string in RAM. Then your original code would work. But it would still use RAM for things that are in practice, constants.

1 Like

(Apologies for saying “best” two times - I just started to write, and thought of each option as I went, without no further thought or edits.)

Lol, no worries.

How about this:

#define TYPEWORD(A) if (keyToggledOn(keyState)) {return Macros.type(PSTR(A));}

const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
  switch (macroIndex) {

   case M_Prog: TYPEWORD("Hello "); break; 
   case M_1: TYPEWORD("Hello "); break; 
   case M_2: TYPEWORD("Hello "); break; 
   case M_3: TYPEWORD("Hello "); break; 
   case M_4: TYPEWORD("Hello "); break; 
   case M_5: TYPEWORD("Hello "); break;
  }
  return MACRO_NONE;
}
1 Like

You could also use a real compile time constant string array, e.g.

constexpr char words[2][4] = { "foo", "bar" };

Beware of the fact that the second dimension must be by one larger than the longest string (to store the \0).

2 Likes