C宏的参数通过参数前缀限制

C macro's argument limiting by argument prefix

我有一组定义的宏如下。

    #define ARM_FRONT_REG   1
    ..............
    #define ARM_REAR_REG   10
    
    
    #define MOTOR_MAIN_REG   1
    ..............
    #define MOTOR_AUX_REG   3
    
    
    #define MOTOR_REGISTER_ADDRESS(register_offset)      \
         (                                               \
                addr = MOTOR_BASE_ADDR * (1 << BITS_PER_MODULE) + register_offset)                  \                               
          )                                              \
    
    #define ARM_REGISTER_ADDRESS(register_offset)      \
         (                                               \
                addr = ARM_BASE_ADDR * (1 << BITS_PER_MODULE) + register_offset)                  \                               
          )                                              \

我正在使用像

这样的宏
ui_address = ARM_BASE_ADDR (ARM_REAR_REG)
ui_address = MOTOR_REGISTER_ADDRESS (MOTOR_MAIN_REG)

我想限制相互混合的宏使用。如果使用如下宏,有没有办法中止编译?

ui_address = ARM_BASE_ADDR (MOTOR_MAIN_REG)
ui_address = MOTOR_REGISTER_ADDRESS (ARM_REAR_REG)

PS : 我简要提到了宏,但实际的宏如下所示,用于从用户应用程序执行寄存器读取写入 Linux 驱动程序。

实际结构:

struct hw_register_struct
{
    int  log_level;
    unsigned int reg_addr;
    unsigned int reg_value;
    char    reg_name [MAX_REG_NAME_LENGTH];
    char    application_info [APP_INFO_LENGTH];
};

此宏验证每个模块的地址是否正确。

 #define CHECK_ADDR_SUB_MODULE(module_index, sub_module, sub_module_bits, offset, max_reg_count)     
        ({                                                                                              
            unsigned int check_ret = 0;                                                                 
            if(offset >= max_reg_count){                                                               
                hw_register.reg_addr = 0;                                                           
                check_ret = 1;                                                                          
            } else {                                                                                    
                hw_register.reg_addr = (module_index * (1 << BITS_PER_MODULE) + (1 << sub_module_bits) * (sub_module) + offset); 
            }                                                                                           
            check_ret;                                                                                  
        })                                                                                              
    

此宏将地址分配给结构中的变量。

    #define SEQUENCER_REGISTER_ADDRESS(register_offset)                                       
        ({                                                                                              
            memset((void *)&hw_register, 0, sizeof(struct hw_register_struct));                 
            if(CHECK_ADDR_SUB_MODULE(MODULE_SEQUENCER, 0, register_offset, SEQ_REG_COUNT)){            
                Logger::Print(ERROR_LEVEL, "Invalid Address | Module : %s | Address : %s", STR(MODULE_SEQUENCER), #register_offset); 
            }                                                                                           
            memcpy(hw_register.reg_name, #register_offset, sizeof(#register_offset));               
            hw_register.reg_addr;                                                                   
        })                                                                                              
    
    

执行调用 ioctl 到 Linux 驱动程序

    #define WRITE_REGISTER_(register_addr, register_value, func, line, log_level_)
        {                                                                                               
            register_addr;                                                                              
            hw_register.reg_value = register_value;                                                 
            hw_register.log_level = log_level_;                                                     
            snprintf(hw_register.application_info, APP_INFO_LENGTH - 1,"%s:%d", func, line);        
            long ret_ioctl = p_IOCTL->IOCTL<struct hw_register_struct>(IOCTL_WRITE, hw_register);  
            if(unlikely(ret_ioctl != 0))                                                                
            {                                                                                                
                Logger::Print(ERROR_LEVEL, "IOCTL WRITE_REGISTER Failed | Reg: %u, Reg Name [ %s ]", hw_register.reg_addr, hw_register.reg_name);
            }                                                                                           
         }    
    
    
    #define WRITE_REGISTER_INFO(register_addr, register_value) WRITE_REGISTER_(register_addr, register_value, __func__, __LINE__, KERN_INFO_LEVEL)

在您的情况下,您可以做的一件事是让接受参数的宏向传递的参数添加名称前缀。例如:

#define ARM_REGISTER_ADDRESS(register_offset)      \
     (                                               \
            addr = ARM_BASE_ADDR * (1 << BITS_PER_MODULE) + ARM_##register_offset)                  \                               
      )

## 将连接 ARM_ 和传递给宏的参数。然后您可以将其用作:

ui_address = ARM_BASE_ADDR (REAR_REG);

ui_address = ARM_BASE_ADDR (MAIN_REG);

会失败,因为 ARM_MAIN_REG 不存在(在您的情况下)。

但我认为即使使用枚举进行类型检查也不能解决您的问题(至少,我不知道允许它的编译器选项)。

如果您需要在预处理器中执行此操作,您可以使用带有变量的标记,然后将其与将扩展为结果的唯一名称连接起来,如下所示:

#define MOTOR_BASE_ADDR  1
#define BITS_PER_MODULE 2

#define ARM_FRONT_REG   (ARM, 1)
#define ARM_REAR_REG    (ARM, 10)

#define MOTOR_MAIN_REG   (MOTOR, 1)
#define MOTOR_AUX_REG    (MOTOR, 3)
    
#define MOTOR_UNIQUE_STRING(x)   x
#define MOTOR_ONLY(a, b)  a##_UNIQUE_STRING(b)
#define MOTOR_REGISTER_ADDRESS(register_offset) \
    ( MOTOR_BASE_ADDR * (1 << BITS_PER_MODULE) + MOTOR_ONLY register_offset )

#define ARM_UNIQUE_STRING_FDASDFSAFDA(x)   x
#define ARM_ONLY(a, b)  a##_UNIQUE_STRING_FDASDFSAFDA(b)
#define ARM_REGISTER_ADDRESS(register_offset) \
    ( ARM_BASE_ADDR * (1 << BITS_PER_MODULE) + ARM_ONLY register_offset )


int main() {
    MOTOR_REGISTER_ADDRESS(MOTOR_MAIN_REG); // all fine
    MOTOR_REGISTER_ADDRESS(ARM_FRONT_REG); // error - sytnax error or ARM_UNIQUE_STRING undeclared
}

选择唯一的标签名称和唯一的字符串基本上可以防止出现奇怪的名称。您可以为函数选择更独特的名称。

您可以使用实际的适当类型并使用实际的函数,具体取决于一种结构类型不能转换为另一种结构类型:

struct arm_register { long v; };
static const struct arm_register ARM_FRONT_REG = {1};
struct motor_register { long v; };
static const struct motor_register MOTOR_MAIN_REG = {1};

#define MOTOR_BASE_ADDR  1
#define BITS_PER_MODULE 2

static inline long MOTOR_REGISTER_ADDRESS(struct motor_register register_offset) {
    return MOTOR_BASE_ADDR * (1 << BITS_PER_MODULE) + register_offset.v;
}

int main() {
    MOTOR_REGISTER_ADDRESS(MOTOR_MAIN_REG); // all fine
    MOTOR_REGISTER_ADDRESS(ARM_FRONT_REG); // error - incompatible type
}

# or with _Generic:

#define MOTOR_REGISTER_ADDRESS_2(x) \
    _Generic((x), struct motor_register: MOTOR_BASE_ADDR * (1 << BITS_PER_MODULE) + x.v)
static const int a = MOTOR_REGISTER_ADDRESS_2(MOTOR_MAIN_REG); // all fine
static const int b = MOTOR_REGISTER_ADDRESS_2(ARM_FRONT_REG); // error - _Generic can't be chosen