Как добавить к имени аргумента в макросе C

#c #protocol-buffers

#c #буферы протокола

Вопрос:

У меня есть странный вариант использования, в котором мне нужно изменить имя переменной, которая была передана в C

 #define SET_PROTOBUF_REPEATED(msg_ptr, field_name, src_data, src_data_length) 
{     
 /*
 msg_ptr->field_name.len = src_data_length;   
 */     

 // I want to do this
 // msg_ptr->n_field_name = src_data_length 

 /*
 msg_ptr->field_name.data = malloc(src_data_length);    
 memcpy(msg_ptr->field_name.data, src_data, src_data_length);     
 */
 msg_ptr->field_name = malloc(src_data_length);    
 memcpy(msg_ptr->field_name, src_data, src_data_length);  
 }
  

Поскольку я использую повторяющийся тип в сообщении буфера протокола, переменная длина определяется как

 n_variable_name
  

вместо

 variable_name.len
  

например.

 variable_name = frequency

// the length have to be n_frequency
  

Причина, по которой я хочу иметь макрос, который делает это, заключается в том, что у меня много сообщений protobuf, повторяющих функциональность функции выше. Для удобства чтения и модульности я думаю, что было бы неплохо определить новую функцию, которая позаботится об этом.

Любые советы будут высоко оценены

Спасибо

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

1. Попробуйте использовать ## оператор конкатенации. Таким образом: msg_ptr->n_##field_name = src_data_length;

2. @Don’Tyouworrychild: я почти уверен, что за обратной косой чертой не должен следовать пробел или что-либо другое, кроме новой строки.

3. @jwodder : Да!! Спасибо!! за напоминание мне. Я запутался, на самом деле я однажды столкнулся с этой проблемой, когда я поместил пробел после обратной косой черты, и мне потребовалось некоторое время, чтобы понять это. До того, как я прочитал ваш комментарий, я вспомнил его наоборот !!. 🙂

Ответ №1:

Канонический способ написания многострочного макроса — это обернуть его внутри do ... while(0) инструкции. Это гарантирует, что макрос работает как единый оператор. Например.:

 #define macro(a,b,c) 
  do { 
    (a) = (b)*(c); 
  } while (0)  /* No trailing semi-colon */

...

macro (x, y, z);
for (i = 0; i < n; i  )
  macro (x, y, z);  /* This wouldn't work properly without do...while(0) */
  

Вы должны заключить параметры вашего макроса в круглые скобки, чтобы избежать неожиданностей, когда кто-то передает неожиданные аргументы, например, macro (t, 1 2, 3 4); вам также нужен вспомогательный макрос для объединения параметров. Возможно, вы ищете что-то вроде этого:

 #define CONCAT(x,y) x ## y

#define SET_PROTOBUF_REPEATED(msg_ptr, field_name, src_data, src_data_length) 
do {                                                      
  (msg_ptr)->CONCAT(n_,field_name) = (src_data_length);   
                                                          
  (msg_ptr)->(field_name) = malloc((src_data_length));            
  memcpy((msg_ptr)->(field_name), (src_data), (src_data_length)); 
} while (0)