Search code examples
cpreprocessor

C macro's argument limiting by argument prefix


I have a set of defined macros as follows.

    #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)                  \                               
          )                                              \

I am using macros like

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

I want to restrict macro usage which is mixed with each other. Is there a way of aborting compiling if macros used as following?

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

PS : I have mentioned macros in brief, But actual macros are as below, which used to perform register reads write to Linux driver from user application.

actual struct :

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];
};

This macro validates the address is correct per module.

 #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;                                                                                  
        })                                                                                              
    

This macro assigns the address to the variable in the struct.

    #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;                                                                   
        })                                                                                              
    
    

Perform calling the ioctl to Linux driver

    #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)

Solution

  • In your case, one thing you can do is have the macros taking arguments adding a name prefix to the argument passed. E.g.:

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

    The ## will concatenate ARM_ and the argument passed to the macro. Then you can use it as:

    ui_address = ARM_BASE_ADDR (REAR_REG);
    

    And

    ui_address = ARM_BASE_ADDR (MAIN_REG);
    

    Would fail because ARM_MAIN_REG doesn't exist (in your case).

    But I don't think typechecking even using enums will solve your issue (at least, I am not aware of a compiler option to allow it).