Trouble accessing an enum in a struct.

I'm getting an error when I tried to add this enum to my struct. Here's the code:

typedef struct Sprites {
  byte timer;
  byte nLength;
  byte nLengthSeq[16];
  byte seqLength;
  bool active;
  
  typedef enum {
  SEQ_STOP = 1,
  SEQ_PLAY = 2,
  SEQ_RECORD = 3,
  CALIBRATION = 4
} state;
} *sprite;

sprite spriteP1[17];

And with this method...

(*spriteP1[0]).state = SEQ_PLAY;

I get an error "invalid use of 'Sprites::state' Error compiling".

Any recommendations how I should do this?

I'm getting an error when I tried to add this enum to my struct.

Why are you doing that? The definition of the enum is NOT an integral part of the struct. Move the definition outside of the struct. Create a field in the struct that is of the type the enum is defining to hold the value.

By the way, it's easier to read the intent if the typedef statement is separate from the struct definition.

given that is the type of the struct?

The typedef statement is hard to follow, wrapped up with the definition of the struct and not following convention.

OP: it's far more normal to have:

struct lowerCaseName
{
};
typedef struct lowerCaseName UpperCaseName;
typedef struct lowerCaseName *pUpperCaseName;

And, the typedef as a pointer doesn't have much use on the Arduino.

PaulS:
Why are you doing that? The definition of the enum is NOT an integral part of the struct. Move the definition outside of the struct. Create a field in the struct that is of the type the enum is defining to hold the value.

I have an array of 17 structs. It's necessary for me to keep track of all the values with 17 copies of this thing and point to the appropriate one, depending on what's going on elsewhere. Like this:

(*spriteP1[selected]).tileRow--;

Yeah now that you mention it there isn't any reason I need to have it in there, I could just create another array.

By the way, the information within is pretty huge - I shortened it here just for clarity.

Delta_G:
Does that compile? Shouldn't it be:

Sprites spriteP1[17];

given that is the type of the struct?

Yes, it works.

PaulS:
The typedef statement is hard to follow, wrapped up with the definition of the struct and not following convention.

OP: it's far more normal to have:

struct lowerCaseName

{
};
typedef struct lowerCaseName UpperCaseName;
typedef struct lowerCaseName *pUpperCaseName;




And, the typedef as a pointer doesn't have much use on the Arduino.

Thanks. I'm just learning this stuff so this is extremely helpful.

Your questions are about several different topics, but I think this is what you are trying to do:

struct Sprite {
  byte timer;
  byte nLength;
  byte nLengthSeq[16];
  byte seqLength;
  bool active;
  
  enum {
    SEQ_STOP = 1,
    SEQ_PLAY = 2,
    SEQ_RECORD = 3,
    CALIBRATION = 4
  } state;
};

Sprite sprites[17];  // "struct Sprite sprites[17]" permitted, but not required

This will allow you to write:

sprites[0].state = SEQ_PLAY;

(Almost, but more on that later in this dissertation...:wink: )

I think you wanted an array that contains 17 sprites, not an array of 17 pointers to sprites, where those sprites would be defined and created elsewhere. Right? And if one of them is "selected", you can use a pointer to one of them:

Sprite *spriteP;

void somefn()
{
  spriteP = &sprites[ selected ];
  spriteP->state = Sprite::CALIBRATION;
  // or less common,  *spriteP.state = Sprite::CALIBRATION;
  //    or  even     (*spriteP).state = Sprite::CALIBRATION;
}

Then all your code uses spriteP, and you just change this pointer when a different sprite is selected.

Simply stated, you don't need the typedef word. Defining the Sprite structure (i.e., listing its members) is also a declaration (i.e., its name). The typedef keyword is used to declare a new "type", not a new struct. It is frequently used to shorten a complicated type name or, as PaulS points out, declare a "pointer" version of another type.

As for what typedef is not, it is not used to declare a variable nor a struct member. In this case, there's no reason for you to have another type name for that structure: Sprite is all you need to refer to this type. Just omit the typedef keyword.

Similarly, you do not need a typedef for the enum, although it is (or was) common. As you have it now, that enum type is actually anonymous. I believe you wanted a member of that anonymous type inside your struct, and that member was called state.

However, it is considered good practice to name your enum types, whether they are declared inside a struct or outside. In this case, I would suggest something like:

enum state_t {    <-- the type name
  SEQ_STOP = 1,
  SEQ_PLAY = 2,
  SEQ_RECORD = 3,
  CALIBRATION = 4
} state;               <-- also declares a variable

This may be confusing because this is really two combined statements:

enum state_t {    <-- define the type called "state_t"
  SEQ_STOP = 1,
  SEQ_PLAY = 2,
  SEQ_RECORD = 3,
  CALIBRATION = 4
};
enum state_t state;     <-- define a variable of that type

And, when nested in the Sprite structure:

struct Sprite {
  byte timer;
  byte nLength;
  byte nLengthSeq[16];
  byte seqLength;
  bool active;
  
  enum state_t {
    SEQ_STOP = 1,
    SEQ_PLAY = 2,
    SEQ_RECORD = 3,
    CALIBRATION = 4
  };
  enum state_t state;     <-- a member of that type, or...
  state_t state;         <-- ...just this, because "enum" is optional
};
  • You may have seen code that also combines a typedef statement with a struct definition:
  typedef struct { int X; int y; } Coord;

This is actually an anonymous struct definition combined with a typedef statement:

  struct anon { int x; int y; }; // a generic struct with 2 int members
  typedef struct anon Coord; // Coord is a separate name for the same struct

This is also explains why there was a compile error. Your original code...

  typedef enum {
  SEQ_STOP = 1,
  SEQ_PLAY = 2,
  SEQ_RECORD = 3,
  CALIBRATION = 4
} state;

...actually does two things: (1) defines an anonymous enum with 4 choices, and (2) declares a type called state. This is not a member, it's a type! Then when you tried to use it, the compiler basically said, "you're using a type name like it was a member name."

Anyway, if you give the enum type a name, you can declare other variables of that type:

void check_state(  Sprite::state_t  last_state  )
{
   if (spriteP->state == last_state)
    sprites[0].state = Sprite::SEQ_STOP;

"But what is that extra 'Sprite::' doing in there?", you ask.

It has to do with another concept, called scoping. Because the enum was declared (and defined) inside the Sprite definition, state_t and the enum choices are "hidden" from casual usage. You must prefix it with the "scope" in which it was declared. That is, **Sprite::**SEQ_STOP.

PaulS makes this suggestion:

PaulS:
The definition of the enum is NOT an integral part of the struct. Move the definition outside of the struct. Create a field in the struct that is of the type the enum is defining to hold the value.

Personally, I don't mind the nested definition, because this enum appears to be a "part" of a Sprite. If there are other things in your sketch that use the same enum choices, then I would pull it out from inside of Sprite. If these 4 choices are unique to a Sprite, I would leave it nested.

Nesting the enum (or any type) is a good way to avoid name "collisions", and it prepares you for the object-oriented features that you may want to use later. For example, if some other library uses the word CALIBRATION, you may get compile errors. By nesting it inside Sprite, other libraries will not force you to rename it. And if anyone decides to reuse your code, it will not force them to avoid a collision. (Namespaces are another way, but this post is already long enough!)

In the short term, pulling the enum definition out may help you understand the difference between "types" and "instances" (i.e., variables of a certain type). So if you had declared the enum outside of the struct Sprite, the state_t name could have been used without the requiring the Sprite:: scope:

enum state_t {
    SEQ_STOP = 1,
    SEQ_PLAY = 2,
    SEQ_RECORD = 3,
    CALIBRATION = 4
  };

struct Sprite {
  byte timer;
  byte nLength;
  byte nLengthSeq[16];
  byte seqLength;
  bool active;

  state_t state;    <-- voilà
};

void start_rec()
{
   sprite[0].state = SEQ_RECORD;  <-- ditto

Clear as mud?

Cheers,
/dev

Wow, this is amazing to have someone so clearly explain what's going on. I've been pecking at this sketch since January and have felt very confused with this struct array, even though it is basically working. Going through your post with a magnifying glass and making some adjustments. More to come. Thank you!

How would I allocate memory by setting up the struct array this way?

struct Sprite {
  byte timer;
  byte nLength;
  byte nLengthSeq[16];
  byte seqLength;
  bool active;
  
  enum {
    SEQ_STOP = 1,
    SEQ_PLAY = 2,
    SEQ_RECORD = 3,
    CALIBRATION = 4
  } state;
};

Sprite sprites[17];

Previously, I had done it like this, by incrementing " i " in a loop.

spriteP1[i] = (struct Sprites *) malloc (sizeof(struct Sprites));

How would I allocate memory by setting up the struct array this way?

You just did:

Sprite sprites[17];

Shouldn't have to use malloc in your example, as you have an array of structs, not an array of pointers to structs.

If you wanted to use malloc that way:

Sprite *spriteP1[17];

spriteP1[i] = (Sprite *) malloc (sizeof(Sprites));

I see, thanks.

Things are working great. But I need a way to set all the values to zero when I initialize the device. And I'd like this process to work the same if I add or remove members. Currently I'm just looping through it and setting each member to zero, so I have to update my initialization script whenever I make minor changes to the array. Any recommendations?

struct parameter {
  int coord_X;
  int coord_Y;
  int coordSequence[2][16];
  bool glideSequence[16];
  bool gliding;
  int glideIterator;
  byte mapColumn;
  byte mapRow; 
  byte mapSequence[2][16];
  byte mapStage;
  byte tileColumn;
  byte tileRow;
  byte editMapColumn;
  byte editMapRow;
  byte editTileColumn;
  byte editTileRow;
  int object;
  int orient;
  byte timer;
  byte noteLength;
  byte noteLengthSequence[16];
  bool noteLengthOverride;
  byte sequenceLength;
  bool onEdge;
  bool active;
  byte stage;
  byte previousStage;
  byte editStage;
  bool gravity;
  int gravity_X;
  int gravity_Y;
  int max_X;
  int max_Y;
  bool edgeBounce;
  bool xBounce;
  bool yBounce;

  enum state_t {
    SEQ_STOP = 1,
    SEQ_PLAY = 2,
    SEQ_RECORD = 3,
  };

  state_t state;

  enum rState_t {
    REC_RESET = 1,
    REC_SAVE = 2,
    REC_UPDATE = 3,
  };

  rState_t rState;

};

parameter sprite[17];

Any recommendations?

Use a class, with a constructor, instead of a struct.

Ok thanks, I'll give it a shot.