Search code examples
cstructinitialization

Initializing and using struct members inside another struct


I need to use a struct (struct1), that was previously declared, inside a new struct (struct2). Also I want to initialize it, and use some of the members of struct1 to initialize other members of struct2.

Specifically, I want to set values for struct1 and use some of those values to define the sizes of other members of struct2

I tried what is shown in the code, but I don't get why it isn't working.

typedef struct ytxModule{
    uint8_t nEncoders;
    uint8_t nDigital;
    uint8_t nAnalog;
    uint8_t nLedsPerControl;
};

typedef struct{
    ytxModule components = {4, 0, 0, 16};

    // encoder pin connections to MCP23S17
    uint8_t encPins[components.nEncoders][2] = {
      {1, 0},  
      {4, 3},   
      {14, 15},  
      {11, 12}   
    };
    // buttons on each encoder
    uint8_t encSwitchPins[components.nEncoders] = { 2, 5, 13, 10 }; 

}ytxE41Module;

The error message I get is:

sketch\headers/modules.h:52:37: error: invalid use of non-static data member '<anonymous struct>::components'

  ytxModule components = {4, 0, 0, 16};

                                     ^

sketch\headers/modules.h:55:18: error: from this location

  uint8_t encPins[components.nEncoders][2] = {

Any help will be appreciated :)


Solution

  • Minimum necessary changes

    You can't mix a typedef with initializers — at least, not in C. You also can't have structure types where the size varies depending on the data in the structure — at least, not without using one (and only one) flexible array member (FAM), but your code attempts to use two variable arrays (so they can't be FAMs).

    You need something more like:

    typedef struct ytxModule{
        uint8_t nEncoders;
        uint8_t nDigital;
        uint8_t nAnalog;
        uint8_t nLedsPerControl;
    } ytxModule;  // Add name for typedef
    
    typedef struct{
        ytxModule components;
        uint8_t encPins[4][2];
        uint8_t encSwitchPins[4]; 
    } ytxE41Module;
    
    ytxE41Module module = {
        .components = {4, 0, 0, 16},
        .encPins = {
          {1, 0},  
          {4, 3},   
          {14, 15},  
          {11, 12}   
        },
        .encSwitchPins = { 2, 5, 13, 10 },
    }; 
    

    This won't change shape if you initialize .components.nEncoders to a value other than 4. Depending on your needs, you could consider replacing the hard-coded 4 with a compile-time changeable value, but you'd also need a way to resize the initializer arrays to match, which is likely to be fiddly at best, inscrutable at worst. It isn't clear how the initializer values are established.

    Using a flexible array member

    If you want to use a flexible array member, you'll have to organize a type to hold an array for the encSwitchPins and encPins data, and use different notation to access the elements of the FAM.

    typedef struct ytxModule{
        uint8_t nEncoders;
        uint8_t nDigital;
        uint8_t nAnalog;
        uint8_t nLedsPerControl;
    } ytxModule;
    
    typedef struct
    {
        ytxModule components;
        struct
        {
            uint8_t encPins[2];
            uint8_t encSwitchPins;
        } pins[]; 
    } ytxE41Module;
    

    You can no longer use an initializer for this data type; you will dynamically allocate the memory and then assign values to the allocated data:

    // Data used for initializing structure
    int n_pins = 4;
    uint8_t encPins[][2] = {
        {  1,  0 },  
        {  4,  3 },   
        { 14, 15 },  
        { 11, 12 },   
    };
    uint8_t switchPins[] = { 2, 5, 13, 10 };
    
    // Allocate and check allocation
    ytxE41Module *mp = malloc(sizeof(*mp) + n_pins * sizeof(mp->pins[0]));
    if (mp == NULL)
        return NULL;
    
    // Assign values to allocated structure
    mp->components = (ytxModule){ n_pins, 0, 0, 16};
    for (int i = 0; i < n_pins; i++)
    {
        for (int j = 0; j < 2; j++)
            mp->pins[i].encPins[j] = encPins[i][j];
        mp->pins[i].encSwitchPins = switchPins[i];
    }
    

    If you tag the embedded structure type, you could initialize an array of that type and use memmove() (or memcpy()) to move copy a separate array into your FAM structure. Etc. It is much simpler if the amount of data in the structure is fixed (e.g. at 4 elements in the array), as shown in the first part of the answer.