Как преобразовать значение аргумента макроса в строку с помощью XMACROs вместо преобразования самого переданного аргумента

#c #for-loop #automation #x-macros

Вопрос:

Я публикую этот вопрос здесь после того, как я провел достаточно исследований в нашем сообществе. Однако я пока не мог найти правильного решения своей проблемы. Итак, я размещаю свой вопрос здесь.

 #define EXPAND_AS_ENUMERATION(a) CODE_##a,
#define EXPAND_AS_ARRAY(a) a,

#define CODE_TABLE(EXPAND)
    EXPAND(0x00E30054uL)
    EXPAND(0x00ED3581uL)
    EXPAND(0x00ED3983uL)
    EXPAND(0x00EE0368uL)
    EXPAND(0x00EE0368uL)
    EXPAND(0x00D01087uL)
    EXPAND(0x00ED4181uL)
    EXPAND(0x00505602uL)


// Actual Event IDs which need to be selected at run time
#define SWC_FAULT_CODE_0x00E30054_EVENT 113
#define SWC_FAULT_CODE_0x00ED3581_EVENT 213
#define SWC_FAULT_CODE_0x00ED3983_EVENT 432
#define SWC_FAULT_CODE_0x00EE0368_EVENT 411
#define SWC_FAULT_CODE_0x00EE0368_EVENT 311
#define SWC_FAULT_CODE_0x00D01087_EVENT 231
#define SWC_FAULT_CODE_0x00ED4181_EVENT 471
#define SWC_FAULT_CODE_0x00505602_EVENT 419

#define prefix_str SWC_FAULT_CODE
#define postfix_str _EVENT

#define Get_EventID_FROM_CODE(code)         ?????   // ex: when code= 0x00ED3581, then it should return SWC_FAULT_CODE_0x00ED3581_EVENT macro or it's value 213
#define Get_EventID_FROM_ENUM(code_enum)    ?????   // ex: when code_enum= CODE_0x00ED3581, then it should return SWC_FAULT_CODE_0x00ED3581_EVENT macro or it's value 213
#define Get_CodeEnumName(code)              ?????   // ex: when code = 0x00ED3581, then it should return DTC_0x00ED3581 which is enum element

typedef enum
{
    CODE_TABLE(EXPAND_AS_ENUMERATION)
    CODE_COUNT
}codeList_t ;


void Cycle_1ms(const uint32 code)
{
    
    // Based on code value, I want to get the macro value of SWC_FAULT_CODE_0xZZZZZZZZ_EVENT. 
    // ex: when code = 0x00ED3581, then eventID = SWC_FAULT_CODE_0x00ED3581_EVENT = 213
    
    uint16 eventID_FromCode = Get_EventID(code);
    
}

void Cycle_1ms_anotherFunc(codeList_t  code_enum)
{
// Based on code value, I want to get the macro value of SWC_FAULT_CODE_0xZZZZZZZZ_EVENT. 
    // ex: when code = 0x00ED3581, then eventID = SWC_FAULT_CODE_0x00ED3581_EVENT = 213
    uint16 eventID_FromCodeEnum = Get_EventID_FROM_ENUM(code_enum);
}

void callback_func(uint32 code, uint8* buf)
{
    
    // here, based on code value Get_CodeEnumName() should provide the enum element for that code. ex: when code= 0x00ED3581, enum_var = DTC_0x00ED3581
    codeList_t enum_var = Get_CodeEnumName(code);
}
 

Здесь CODE_TABLE будет изменяться от проекта к проекту и количество элементов в этой таблице. Прочитав несколько статей и тем по Xmacros, я подумал об использовании здесь концепции Xmacros, чтобы избежать зацикливания и интенсивного использования оперативной памяти .

Комментарии:

1. В ваших функциях code это переменная. Препроцессор не может знать его значение. Возможно, вы могли бы превратить свои Get_ макросы в таблицу поиска или в функцию, тело которой является еще одним расширением вашего макроса X, например case X: return SWC_FAULT_CODE_##X##_EVENT; .

Ответ №1:

Вы не можете использовать макросы X с вводом во время выполнения, они расширяются во время компиляции. Поэтому, если вам нужно выполнить поиск таблиц на основе значений времени выполнения, они не являются решением.

Однако вы можете использовать макросы X для создания таблицы поиска, а затем использовать константу времени компиляции магического числа для расширения в перечисление, которое, в свою очередь, соответствует индексу таблицы поиска:

 #define GET_EVENT_ID(code) LOOKUP[CODE_##code]
 

GET_EVENT_ID расширяется до LOOKUP[CODE_0x00ED3581] чего-то вроде доступа к массиву.

Кроме того, «события» могут быть объединены вместе с кодами в одном списке макросов X, даже если вам не нужно использовать их в каждом макросе. Также обратите внимание, что у вас есть дубликаты элементов в списке макросов X, которые не будут работать, так как вы планируете использовать его для создания уникальных констант перечисления. Мы можем изменить это вот так:

 #define CODE_TABLE(X)      
/*  code          event */ 
  X(0x00E30054uL, 113)     
  X(0x00ED3581uL, 213)     
  X(0x00ED3983uL, 432)     
  X(0x00EE0368uL, 411)     
  X(0x00D01087uL, 231)     
  X(0x00ED4181uL, 471)     
  X(0x00505602uL, 419)     
 

Затем создайте SWC_FAULTS в качестве констант перечисления вместо #определяет:

 // create SWC_FAULT_CODE_0x00ED3581_EVENT constants:
#define SWC_FAULT(code,event) SWC_FAULT_CODE_##code##_EVENT = event,
enum 
{
  CODE_TABLE(SWC_FAULT)
};
 

А затем перечисление, которое нужно использовать для индекса таблицы подстановки:

 // create enums like CODE__0x00ED3581:
#define EXPAND_AS_ENUMERATION(code, event) CODE_##code,
typedef enum
{
  CODE_TABLE(EXPAND_AS_ENUMERATION)
  CODE_COUNT
}code_list_t ;
 

Полный пример:

 #include <inttypes.h>
#include <stdio.h>

#define CODE_TABLE(X)      
/*  code          event */ 
  X(0x00E30054uL, 113)     
  X(0x00ED3581uL, 213)     
  X(0x00ED3983uL, 432)     
  X(0x00EE0368uL, 411)     
  X(0x00D01087uL, 231)     
  X(0x00ED4181uL, 471)     
  X(0x00505602uL, 419)     


// create SWC_FAULT_CODE_0x00ED3581_EVENT constants:
#define SWC_FAULT(code,event) SWC_FAULT_CODE_##code##_EVENT = event,
enum 
{
  CODE_TABLE(SWC_FAULT)
};

// create enums like CODE__0x00ED3581:
#define EXPAND_AS_ENUMERATION(code, event) CODE_##code,
typedef enum
{
  CODE_TABLE(EXPAND_AS_ENUMERATION)
  CODE_COUNT
}code_list_t ;

// create look-up table containing SWC_FAULT_CODE_0x00ED3581_EVENT
// use designated initializers to get [CODE__0x00ED3581] = SWC_FAULT_CODE_0x00ED3581_EVENT
#define LOOKUP_TABLE(code,event) [CODE_##code] = SWC_FAULT_CODE_##code##_EVENT,
static const uint32_t LOOKUP[CODE_COUNT] = 
{
  CODE_TABLE(LOOKUP_TABLE)
};
_Static_assert(sizeof LOOKUP/sizeof *LOOKUP == CODE_COUNT, "LOOKUP table corrupt");


#define GET_EVENT_ID(code) LOOKUP[CODE_##code]

int main()
{
  printf("0x%.8" PRIX32 " = %"PRIu32 "n", 0x00ED3581uL, GET_EVENT_ID(0x00ED3581uL));

  return 0;
}
 

Вывод: 0x00ED3581 = 213 .

В gcc x86 вся таблица может быть оптимизирована, оставив только это:

     mov     edx, 213
    mov     esi, 15545729
    xor     eax, eax
    mov     edi, OFFSET FLAT:.LC0
 

Комментарии:

1. но это работает только в том случае, если code это литерал, а не переменная

2. @tstanisl Да, это действительно то, что говорится в первых двух предложениях этого ответа.

3. но это не сработает даже при незначительных изменениях ввода, таких как: GET_EVENT_ID(0x00ED3581uL) vs GET_EVENT_ID(0x00ED3581u) vs GET_EVENT_ID(0x00ED3581ul) vs GET_EVENT_ID(0x00ED3581) или GET_EVENT_ID(0xED3581)

4. На самом деле мне было бы проще сделать #define GET_EVENT_ID(code) SWC_FAULT_CODE_##code##_EVENT , так как это расширилось бы до истинной константы, а не какой-то записи в таблице поиска

5. @tstanisl Да, так что, если вы хотите, чтобы вызывающий абонент использовал всевозможные версии, возможно, отбросьте ul и позвоните UINT32_C(0x00ED3581) изнутри макросов. Есть всевозможные улучшения, которые можно сделать, я только что продемонстрировал примерную концепцию.

Ответ №2:

Вы можете расширить Get_EventID_FROM_CODE как очень длинную цепочку троичных операторов ( ?: ). XMacro может быть расширен внутри другого макроса, выполняющего этот трюк.

Это позволит вам избежать использования функций. Кроме того, решение также принимает любой формат целочисленного литерала и переменных. Он расширяется до истинной константы C, если это возможно. Единственным недостатком является оценка выражений аргументов, но это обычная проблема для макросов.

 #include <inttypes.h>
#include <stdio.h>

#define CODE_TABLE(X, ...)              
  X(0x00E30054uL, 113, __VA_ARGS__)     
  X(0x00ED3581uL, 213, __VA_ARGS__)     
  X(0x00ED3983uL, 432, __VA_ARGS__)     
  X(0x00EE0368uL, 411, __VA_ARGS__)     
  X(0x00D01087uL, 231, __VA_ARGS__)     
  X(0x00ED4181uL, 471, __VA_ARGS__)     
  X(0x00505602uL, 419, __VA_ARGS__)     

// expands to -1 for invalid code
#define GET_EVENT_ID_(CODE,ENUM,VAR) (VAR) == (CODE) ? ENUM :
#define GET_EVENT_ID(code_var) (CODE_TABLE(GET_EVENT_ID_, code_var) -1)

int main()
{
#define DBG(CODE) 
  printf("%s 0xlx = %dn", #CODE, (unsigned long)(CODE), GET_EVENT_ID(CODE))

  DBG(0x00ED3983uL);
  DBG(0x00ED3983);
  DBG(0xED3983);
  int32_t x = 0x00D01087uL;
  DBG(x);

  // expand to a true constant if possible
  struct X { char x[GET_EVENT_ID(0xED3983)]; };
  printf("%zin", sizeof(struct X));

  return 0;
}
 

Производит ожидаемый результат:

 0x00ED3983uL 0x00ed3983 = 432
0x00ED3983 0x00ed3983 = 432
0xED3983 0x00ed3983 = 432
x 0x00d01087 = 231
432