Type: LanguageService
I'm working on a macro heavy C firmware project and am having issues with nested macros that fail to correctly expand.
To Reproduce
#define OS_DEFINE_PROCESS(M_PID) _OS_DEFINE_PROCESS(M_PID)
#define _OS_DEFINE_PROCESS(P_PIDN, P_PRIO, P_TYPE, P_FUNC) \
extern void(P_FUNC)(void); \
static const OS_cpi_t P_FUNC##_CPI = { \
.prio = P_PRIO, \
.type = P_TYPE, \
.process_foo = P_FUNC};
#define PROCESS_PID 1, 2, PT_MESSAGE, pf_some_function
OS_DEFINE_PROCESS(PROCESS_PID);
Expected behavior
I would expect the macro to correctly expand to something like
extern void(pf_some_function)(void);
static const OS_cpi_t pf_some_function_CPI = {
.prio = 1,
.type = 2,
.process_foo = pf_some_function};
Screenshots

I'd be glad to provide more informations if needed
Hi @fnafnio . Does this compile without error for you? I'm seeing that this does not compile with cl.exe or g++. Though, clang appears to accept it.
typedef struct {
int prio;
int type;
void (*process_foo)();
} OS_cpi_t;
#define PT_MESSAGE 1
#define OS_DEFINE_PROCESS(M_PID) _OS_DEFINE_PROCESS(M_PID)
#define _OS_DEFINE_PROCESS(P_PIDN, P_PRIO, P_TYPE, P_FUNC) \
extern void(P_FUNC)(void); \
static const OS_cpi_t P_FUNC##_CPI = { \
.prio = P_PRIO, \
.type = P_TYPE, \
.process_foo = P_FUNC};
#define PROCESS_PID 1, 2, PT_MESSAGE, pf_some_function
OS_DEFINE_PROCESS(PROCESS_PID); // <- PROCESS_PID resolves first, into multiple arguments
Specifically, it looks like PROCESS_PID will first be expanded into multiple arguments before being passed to OS_DEFINE_PROCESS. However, OS_DEFINE_PROCESS accepts only a single argument, resulting in the other values being blank. This would explain why the projected expansion of the macro does not include values for those arguments.
It does indeed compile with both Keil ARM compiler v5.04 and GNU Arm Embedded Toolchain
Version 9-2019-q4-major, I haven't tried different compilers yet as those are the only ones relevant to the project.
So this might have something to do with Macro expansion order? Shouldn't, after all preprocessor passes, all macros just be fully expanded? Guess I need to read up on the preprocessor
Edit: Fixed the example
#include <stdint.h>
typedef enum os_processtype_enum_t
{
PT_MESSAGE = 0,
PT_STARTBIT = 1,
PT_TIMEOUT = 2,
PT_NOPROCESS = 3
} os_processtype_t;
typedef struct os_cpi_struct_t
{
void (*process_foo)(void);
uint32_t prio;
os_processtype_t type;
} os_cpi_t;
#define os_DEFINE_PROCESS(M_PID) _os_DEFINE_PROCESS(M_PID)
#define _os_DEFINE_PROCESS(P_PIDN, P_PRIO, P_TYPE, P_FUNC) \
extern void(P_FUNC)(void); \
static const os_cpi_t P_FUNC##_CPI = { \
.prio = P_PRIO, \
.type = P_TYPE, \
.process_foo = P_FUNC};
#define PROCESS_PID 9, 10, PT_MESSAGE, pf_some_function
os_DEFINE_PROCESS(PROCESS_PID);
the os_DEFINE_PROCESS(PROCESS_PID); line at the end gets expanded to
extern void(pf_some_function)(void); static const os_cpi_t pf_some_function_CPI = { . prio = 10, . type = PT_MESSAGE, . process_foo = pf_some_function};;
This was tested with Keil, the last code segment is the actual output of the preprocessor listing. The function pf_some_function(void) is defined in another file, which shouldn't matter here
Hi @fnafnio . It looks like the macro expansion behavior differs between compilers. With the code in your most recent reply above, I see MSVC (cl.exe) reports an error on the squiggled line, but gcc does not. If I switch to using "gcc-x64" intelliSense mode, the squiggle is removed.
Are you setting the intelliSense mode? You can use the "C/C++: Log Diagnostics" command to see what IntelliSense mode was used for a file. To set the intelliSense mode to gcc x64, use the following in your configuration in c_cpp_properties.json :
"intelliSenseMode": "gcc-x64
I was setting intellisenseMode to gcc-x64, but as I gathered from the Log Diagnostics output, the extension defaulted to "msvc-x64" as if I hadn't changed it.
Setting the compilerPath to, in this case, an up to date version of MinGW-gcc the squiggles.

Now both macros are expanded, but without expanding the next level _os_DEFINE_PROCESS like this

Other problems with macro expansion (mainly involving CMSIS defines) are now also working as expected.
Thank you for the help with this issue!
One last question before closing this issue, is there or will there be an option to show the macros fully expanded?
One last question before closing this issue, is there or will there be an option to show the macros fully expanded?
Hi @fnafnio . I'm able to repro the issue with the macro not being fully expanded on hover, with Visual Studio. (the C/C++ Extension shares common code for IntelliSense with VS). I've opened a bug against VS here: https://developercommunity.visualstudio.com/content/problem/923319/in-gcc-intellisense-mode-nested-macros-may-not-be.html