Home > Software engineering >  Dereferencing a pointer at runtime in c
Dereferencing a pointer at runtime in c

Time:01-08

I am trying to dereferencing a pointer, according to the data-type passed to the function, just to learn.

code 1: First Try. Oviously it didn't work.

typedef enum
{
    bits_8,
    bits_16,
    bits_32
}bits_width;


inline int32_t write_data_spi1_enhanced_buffer_x_bits( void* data_address, uint32_t size, bits_width data_type )
{       
    switch( data_type )
    {
        case bits_8:
            spi1_change_mode_8_bits();
            uint8_t* dt = (uint8_t*)data_address; 
        break;

        case bits_16:
            spi1_change_mode_16_bits();
            uint16_t* dt = (uint16_t*)data_address;   
        break;

        case bits_32:            
            spi1_change_mode_32_bits();
            uint32_t* dt = (uint32_t*)data_address;    
        break;
        
        default:
            return -1;
        break;
    }
        
    for( uint32_t i = 0 ; i < size ; i   )
    {       
        while( SPI1STATbits.SPITBF );    
        SPI1BUF = *dt  ;  // Write the data out to the SPI1 peripheral. *dt   = dt[i].
    }
    
    return 0;
}

code 2: Looks OK(compile). Not tested in hardware.

typedef enum
{
    bits_8,
    bits_16,
    bits_32
}bits_width;


inline int32_t write_data_spi1_enhanced_buffer_x_bits( uint8_t* data_address, uint32_t size, bits_width data_type )
{       
    uint32_t x;
    
    switch( data_type )
    {
        case bits_8:
            spi1_change_mode_8_bits();
            x = 1; 
        break;

        case bits_16:
            spi1_change_mode_16_bits();
            x = 2;   
        break;

        case bits_32:            
            //spi1_change_mode_32_bits();
            x = 4;    
        break;
        
        default:
            return -1;
        break;
    }
        
    for( uint32_t i = 0 ; i < size ; i   )
    {       
        while( SPI1STATbits.SPITBF );    
        SPI1BUF = *( data_address   (i*x) );  // Write the data out to the SPI1 peripheral.
    }
    
    return 0;
}

code 3: I did not want to write the for loop 3 times(one for each data type).

typedef enum
{
    bits_8,
    bits_16,
    bits_32
}bits_width;


inline int32_t write_data_spi1_enhanced_buffer_x_bits( void* data_address, uint32_t size, bits_width data_type )
{       
    switch( data_type )
    {
        case bits_8:
            spi1_change_mode_8_bits();
            uint8_t* dt = (uint8_t*)data_address; 

            for( uint32_t i = 0 ; i < size ; i   )
            {       
                while( SPI1STATbits.SPITBF );    
                SPI1BUF = *dt  ;  // Write the data out to the SPI1 peripheral. *dt   = dt[i].
            }

        break;

        case bits_16:
            spi1_change_mode_16_bits();
            uint16_t* dt = (uint16_t*)data_address;   

            for( uint32_t i = 0 ; i < size ; i   )
            {       
                while( SPI1STATbits.SPITBF );    
                SPI1BUF = *dt  ;  // Write the data out to the SPI1 peripheral. *dt   = dt[i].
            }

        break;

        case bits_32:            
            spi1_change_mode_32_bits();
            uint32_t* dt = (uint32_t*)data_address;    

            for( uint32_t i = 0 ; i < size ; i   )
            {       
                while( SPI1STATbits.SPITBF );    
                SPI1BUF = *dt  ;  // Write the data out to the SPI1 peripheral. *dt   = dt[i].
            }
        break;
        
        default:
            return -1;
        break;
    }

    return 0;        
}

How to make code 1 work ?

Or is there a better solution ?

Thank`s.

CodePudding user response:

You could create 3 separate functions using a macro:

#define TFUNC(X)                                     \
    inline void funcdef_##X(X* dt, uint32_t size) {  \
        spi1_change_mode_##X();                      \
        for (uint32_t i = 0; i < size; i  ) {        \
            while (SPI1STATbits.SPITBF)              \
                ;                                    \
            SPI1BUF = *dt  ;                         \
        }                                            \
    }

TFUNC(uint8_t)
TFUNC(uint16_t)
TFUNC(uint32_t)

Note: You'd have to rename spi1_change_mode_8_bits into spi1_change_mode_uint8_t etc. for the above to work.

Then use a _Generic as a front-end:

#define write_data_spi1_enhanced_buffer_x_bits(X,S) \
             _Generic((X),                          \
                uint8_t* : funcdef_uint8_t,         \
               uint16_t* : funcdef_uint16_t,        \
               uint32_t* : funcdef_uint32_t)        \
             (X, S)

You'd then call it by a cast of data_address to the proper type and it'll select the correct function to call. Example:

inline int32_t rite_data_spi1_enhanced_buffer(void* data_address, uint32_t size,
                                              bits_width data_type) {
    switch (data_type) {
    case bits_8:
        write_data_spi1_enhanced_buffer_x_bits((uint8_t*)data_address, size);
        break;
    case bits_16:
        write_data_spi1_enhanced_buffer_x_bits((uint16_t*)data_address, size);
        break;
    case bits_32:
        write_data_spi1_enhanced_buffer_x_bits((uint32_t*)data_address, size);
        break;
    default:
        return 1;
    }
    return 0;
}

CodePudding user response:

I do not like this C "generic" syntax.

I would simple:

  1. Avoid pointer punning
  2. Set the register in the switch() ... case
inline int32_t write_data_spi1_enhanced_buffer_x_bits( void* data_address, uint32_t size, bits_width data_type )
{       
    unsigned char *uda = data_address;
    uint16_t u16;
    uint32_t u32;
    
    for( uint32_t i = 0 ; i < size ; i   )
    {       
        while( SPI1STATbits.SPITBF );
            switch( data_type )
            {
            case bits_8:
                SPI1BUF = uda[i];
                break;

            case bits_16:
                memcpy(&u16, uda   sizeof(u16) * i, sizeof(u16));
                SPI1BUF = u16;
                break;

            case bits_32:            
                memcpy(&u32, uda   sizeof(u32) * i, sizeof(u32));
                SPI1BUF = u32;
                break;
            
            default:
                return -1;
            break;
            }    
    }
}

memcpy will be optimized out on targets not needing special alignment.

  • Related