EDIT: I think this fits better in the C++ Stack Overflow, so I'm going to re-post this there.
I am working on a menu system with #define
macros (inspired by the Marlin firmware on my 3D printer) I am attempting to generalize each menu item with the macro #define MENU_ITEM_EDIT()
. The full code is really long, so I am leaving out chunks of code of compactness. However, you can find the full code here: https://pastebin.com/h24itG9y
void draw_row(int row, char* value) { /* Stuff here*/ } // Draws row
char* uiToStr(int x) { /* Stuff here*/ } // Converts unsigned int to a string
#define disp_array(display, value) display[value]
#define disp_func(display, value) display(value)
#define disp_char(display, value) display
#define MENU_ITEM_EDIT(min, max, rate, display, displayType, width, label, value) \
// A bunch of stuff
draw_row(rowNum, disp_ ## displayType(display, value)); \
// More stuff
Using the code macro would look like:
// Values
const char item1[] = "Item 1", item2[] = "Item 2", item3[] = "Item 3";
const char* const itemList[] = {item1, item2, item3};
int currentItem = 0;
uint8_t item1Val = 10;
void menu1()
{
START_MENU();
MENU_ITEM_EDIT(0, 3, 1, itemList, array, 6, "Item:", currentItem);
MENU_ITEM_EDIT(0, 255, 1, uiToStr, func, 3, "Item 1 Value:", item1Val);
END_MENU();
}
Which displays as:
Item: Item 1
Item 1 Value: 10
For convenience in the declarations of my menus, I am trying to use common menu types, such as an uint8_t
that ranges from 0 to 255. Then, in the menu, I would use __u255_t
instead of the first 6 arguments.
#define __u255_t 0, 255, 1, uiToStr, func, 3
void menu2()
{
int item2Val = 10;
START_MENU();
MENU_ITEM_EDIT(__u255_t, "Item 2 Value", item2Val);
END_MENU();
}
Previously, I had it set up such that every time I wanted a different type of menu, even if I would only use it once, I had to define a type for it and how it should evaluate in the macro. This way, I can use 1 argument instead of 6 for commonly recurring types in rows. However, I still want the ability to use the 6 argument option when I have a row that has a unique set of arguments, such as a different range or list of display strings.
However, in this example, the program won't compile because it places __u255_t
into min
in the MENU_ITEM_EDIT()
macro, rather than expanding __u255_t
into its 6 values and placing them into the first 6 arguments of MENU_ITEM_EDIT()
. The macro evaluates as:
MENU_ITEM_EDIT(__u255_t, "Item 2 Value", item2Val);
// Evaluates as:
MENU_ITEM_EDIT( (0, 255, 1, uiToStr, func, 3), "Item 2 Value", item2val, , , , , );
Note: I put an extra set of parentheses to make it more clear to understand.
But I want:
MENU_ITEM_EDIT(__u255_t, "Item 2 Value", item2Val);
// Evaluates as:
MENU_ITEM_EDIT(0, 255, 1, uiToStr, func, 3, "Item 2 Value", item2Val);
I thought about using a struct
, however I'm not sure that I can because depending on the row, the value
string in draw_row()
could be a function or an array. Does anyone have any advice on how to solve this problem?
Also, please let me know if I need to add any more code to make my problem more clear.
1 Answer 1
You need to turn your macro itself into a macro. Sounds odd, I know. But take the following example:
#define foo bar, baz
#define foobar(X, Y, Z) something(X, Y, Z);
#define boofar(...) foobar(__VA_ARGS__)
boofar(foo, 4)
boofar
is a macro that takes a variable argument list. That expands out into a call for your actual macro call, expanding foo
in the process. Then your actual macro gets expanded out with the real arguments already in here. The point here is that foo
is expanded before being passed to the macro that needs the multiple arguments that are inside foo
.
So the expansion chain is actually:
boofar(foo, 4) ->
foobar(bar, baz, 4) ->
something(bar, baz, 4);
Running it through cpp
and you get:
# 1 "t.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "t.c"
something(bar, baz, 4);
including variable-length parameter lists
-- actually they do:__VA_ARGS__
and#define foo(...)