Search code examples
ctypedefunionsvoid-pointers

Attempting to allocate a union to a void* in a struct


The goal is to create a structure that contains 3 fields. One of those fields needs to be a uint32_t union but the flags will be different for each element. I believe the best way to do this is to create a separate union for each peripheral and assign it to a void pointer in the parent struct (somehow).

The Current Plan:

//--------------Register for AAA
typedef union
{
    struct
    {
        uint32_t spare  : 32;  //< Bits 0-31 not used
    }Bits;
    
    uint32_t FullData;
}ABC_EMPTY_DATA;
ABC_EMPTY_DATA AAA_Data;

//--------------Register for BBB
typedef union
{
    struct
    {
        uint32_t use32BitColor          :  1;  //< Bit0 
        uint32_t enableTimer            :  1;  //< Bit1 
        uint32_t fooTheBar              :  1;   //< Bit2
        uint32_t spare                  : 29;  //< Bits 3-31 not used
    }Bits;
    
    uint32_t FullData;
}ABC_BBB_DATA;
ABC_EMPTY_DATA BBB_Data;

//--------------Register for CCC
typedef union
{
    struct
    {
        uint32_t useInternalFrameTick   :  1;  //< Bit0 
        uint32_t useExternalFrameTick   :  1;  //< Bit1 
        uint32_t spare                  : 30;  //< Bits 2-31 not used
    }Bits;
    
    uint32_t FullData;
}ABC_CCC_DATA;
ABC_CCC_DATA CCC_Data;  

//-------The Peripherals with register element
typedef struct
{
    char                *Name;
    unsigned int        HexCmd;
    void                *Data; //Connect to corasponding XXXX_Data  
    
}ABC_dataStructs;

//----- Initializing Peripherals
typedef struct
{
    ABC_dataStructs AAA  = {.Name = "AAA",  .HexCmd = 0x00,     .Data = (void*) &AAA_Data}; 
    ABC_dataStructs BBB  = {.Name = "BBB",  .HexCmd = 0x00,     .Data = (void*) &BBB_Data}; 
    ABC_dataStructs CCC  = {.Name = "CCC",  .HexCmd = 0x00,     .Data = (void*) &CCC_Data}; 
    
}ABC_CommandList;

void main(){
    ABC_CommandList everything;

    everything.CCC.FullData = 0x000F;
    everything.BBB.use32BitColor = 0;
    int myint = everything.AAA.HexCmd;
}

Currently, I am having initializeing the values within the struct ABC_dataStructs AAA = {.Name = "AAA", .HexCmd = 0x00, .Data = (void*) &AAA_Data}; is invalid

Also and more importantly the assigning of the union is not working as intended. The current result only allows for everything.AAA.Data without being able to assign/read individual bits.

I also tried to explain the intent of the SW in order to avoid an "XY problem" and if I am approaching this wrong or you think of something better let me know. Thank You.

Note: C struct, union pointer to struct has another approach of putting structs in the unions instead of unions to the structs, but it doesn't help with my main issue of assigning these to a void*


Solution

  • The problem you're having with the initialization is that you're defining a type and attempting to initialize it as if it's a variable at the same time. You need to first define the struct, then initialize an instance of it:

    typedef struct
    {
        ABC_dataStructs AAA; 
        ABC_dataStructs BBB; 
        ABC_dataStructs CCC; 
    }ABC_CommandList;
    
    void main(){
        ABC_CommandList everything = {
            .AAA  = {.Name = "AAA",  .HexCmd = 0x00, .Data = &AAA_Data},
            .BBB  = {.Name = "BBB",  .HexCmd = 0x00, .Data = &BBB_Data},
            .CCC  = {.Name = "CCC",  .HexCmd = 0x00, .Data = &CCC_Data},
        };
        ... 
    

    Note also that it's not required to cast to/from a void *.

    Then the problem here:

    everything.CCC.FullData = 0x000F;
    everything.BBB.use32BitColor = 0;
    

    Is that CCC and BBB are of type ABC_dataStructs, and that type does not have fields named FullData or use32BitColor. You would need to cast the Data member to the proper pointer type, then dereference that:

    ((ABC_CCC_DATA *)everything.CCC.Data)->FullData = 0x000F;
    ((ABC_BBB_DATA *)everything.BBB.Data)->use32BitColor = 0;
    

    All that being said, there's a better way of modeling this data. What you really want is a union of the 3 register types. Then ABC_dataStructs would contain that union along with a separate field which flags which union to use.

    typedef struct
    {
        uint32_t spare  : 32;  //< Bits 0-31 not used
    } ABC_EMPTY_DATA;
    
    typedef struct
    {
        uint32_t use32BitColor          :  1;  //< Bit0 
        uint32_t enableTimer            :  1;  //< Bit1 
        uint32_t fooTheBar              :  1;   //< Bit2
        uint32_t spare                  : 29;  //< Bits 3-31 not used
    } ABC_BBB_DATA;
    
    typedef struct
    {
        uint32_t useInternalFrameTick   :  1;  //< Bit0 
        uint32_t useExternalFrameTick   :  1;  //< Bit1 
        uint32_t spare                  : 30;  //< Bits 2-31 not used
    } ABC_CCC_DATA;
    
    typedef struct
    {
        char                *Name;
        unsigned int        HexCmd;
        union {
            ABC_EMPTY_DATA AAA;
            ABC_BBB_DATA   BBB;
            ABC_CCC_DATA   CCC;
            uint32_t FullData;
        };
    }ABC_dataStructs;
    

    Then you can define the following:

        ABC_CommandList everything = {
            .AAA  = {.Name = "AAA",  .HexCmd = 0x00, .AAA = {}},
            .BBB  = {.Name = "BBB",  .HexCmd = 0x00, .BBB = { .use32BitColor = 0 }},
            .CCC  = {.Name = "CCC",  .HexCmd = 0x00, .FullData = 0x000F },
        };