'inherit' struct types?

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

  I want to create 2 types which share a base set of attributes but have slight differences. Obviously I can just do this:

 

typedef struct mob_type {
    byte glyph;
    point position;
    
    byte hitpoints;
    
    byte attack;
    byte defence;
    byte damage;
    
    byte num_actions;
    
    byte tactics;
    
    byte dead;
} mob_type;

typedef struct player_type {
	byte glyph;
    point position;
    
    byte hitpoints;
    
    byte attack;
    byte defence;
    byte damage;
    
    byte num_actions;
    
    items *inventory[12];
    
    byte dead;
} player_type;

Notice player_type has an inventory and mob_type has tactics.

 

This looks untidy to me, what I'd really like is to be able to define a generic 'mob' type and then a player is just one of those with an extra inventory attribute.

 

I did some research and found that you can in theory do this:

 

typedef struct Base
{
    // base members
} Base_t;

typedef struct
{
   struct Base;  //anonymous struct

   // derived members

} Derived_t;

but Atmel Studio 7 doesn't like that at all.

 

I want to be able to access the attributes directly for variables of each type (I want player.hitpoints not player.base.hitpoints).

 

Is this possible in the AVR world, or should I just duplicate my type definition?

 

Thanks

-Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MalphasWats wrote:
but Atmel Studio 7 doesn't like that at all.
Well for one thing it's not "Atmel Studio" - that's just a debugger and an editor. The thing that might not like it is the GCC compiler for AVR. If it doesn't then tells us how it expresses this view?

 

I'd personally go with something like:

typedef struct  {
    byte glyph;
    point position;
    
    byte hitpoints;
    
    byte attack;
    byte defence;
    byte damage;
    
    byte num_actions;
    
    byte tactics;
    
    byte dead;
} mob_base_t;

typedef struct {
    mob_base_t base;
    byte tactics;
} mob_tactics_t;

typedef struct {
    mob_base_t base;
    items * inventory[12];
} mob_inv_t;

// then stuff like:

mob_tactics_t tactics;
mob_inv_t invent;

tactics.base.attack = 37;
tactics.tactics = 51;

invent.base.num_actions = 3;
items foo;
foo.item1 = 'A';
invent.inventory[4] = foo;

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

The thing that might not like it is the GCC compiler for AVR. If it doesn't then tells us how it expresses this view?

 

Sorry, you are correct of course. GCC doesn't like it, but it throws about 57 errors that all point in all sorts of directions leading me to believe that what I had done was REALLY not sensible, hence me glossing over that bit!

 

clawson wrote:

I'd personally go with something like:

typedef struct  {
    byte glyph;
    point position;

    byte hitpoints;

    byte attack;
    byte defence;
    byte damage;

    byte num_actions;

    byte tactics;

    byte dead;
} mob_base_t;

typedef struct {
    mob_base_t base;
    byte tactics;
} mob_tactics_t;

typedef struct {
    mob_base_t base;
    items * inventory[12];
} mob_inv_t;

// then stuff like:

mob_tactics_t tactics;
mob_inv_t invent;

tactics.base.attack = 37;
tactics.tactics = 51;

invent.base.num_actions = 3;
items foo;
foo.item1 = 'A';
invent.inventory[4] = foo;

 

Thank you - I was hoping I wouldn't have to go that route as it means changing a lot of code elsewhere. Since there's only ever going to be 1 inventory (the player's) I might just cheat and make it a separate variable from the 'mob' stuff. It's not as tidy but it'll probably save me work. I'm really scraping the bottom of the flash memory barrel now so I'm trying to be as efficient as I can :(

 

It's times like this that I really miss Python!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Take a look at unions.  The common pieces can be members of the structure, with unique items in the union.  The drawback is the union's size will be the size of the largest union member.  Something like this:

typedef struct {
    byte glyph;
    point position;
    
    byte hitpoints;
    
    byte attack;
    byte defence;
    byte damage;
    
    byte num_actions;

    union {
        byte tactics;
        items *inventory[12];
    } unique;
    
    byte dead;
} character_type;

 

Usage would look like this:

 

character_type mob1;

mob1.hitpoints = 100;
mob1.unique.tatics = TACTIC_SWARMING;
mob1.dead = 0;


character_type player1;

player1.hitpoints = 200;
player1.unique.inventory[0] = NULL;
player1.unique.inventory[1] = NULL;
...
player1.dead = 0;

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MalphasWats wrote:
it throws about 57 errors that all point in all sorts of directions

Go on: just look at the first reported error first - almost certainly, that will be the cause of the other 56.

 

Exactly like Python gives you a  (possibly long)  trace-back of all the files through which an exception was propagated.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ScottMN wrote:
Usage would look like this:
Actually, if -std=gnu99 you can use the GNU extension of anonymous members in this case and drop the name on the union.

    byte num_actions;

    union {
        byte tactics;
        items *inventory[12];
    } unique;

    byte dead;

Then:

mob1.unique.tatics = TACTIC_SWARMING;
player1.unique.inventory[0] = NULL;

In fact you might be able to extend it further:

    union {
        struct {
            byte tactics;
            byte unused_padding[23];
        };
        items *inventory[12];
    } ;

to keep the union variants the same length (I'm assuming 16 bit pointers in this).

Last Edited: Tue. Jun 12, 2018 - 03:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another slightly off the wall "solution":

// avr.cpp
#include <avr/io.h>

typedef unsigned char byte;

struct items {
    int n;
    char c;
};

struct point {
    int x;
    int y;
};

struct mob_type {
    byte glyph;
    point position;

    byte hitpoints;

    byte attack;
    byte defence;
    byte damage;

    byte num_actions;

    byte tactics;

    byte dead;
};

struct tactic : mob_type {
    byte tactic;
};

struct invent : mob_type {
    items *inventory[12];
};

int main(void) {
    tactic foo;
    invent bar;
    items baz = { 12345, 'C'};
    
    foo.attack = 1;
    foo.tactic = 7;
    
    bar.defence = 1;
    bar.inventory[3] = &baz;
    
    while(1);
}

(yes folks, it's a different programming language!).

 

Builds without error:

D:\atmel_avr\avr8-gnu-toolchain\bin>avr-gcc -Os -mmcu=atmega16 avr.cpp -o avr.elf

D:\atmel_avr\avr8-gnu-toolchain\bin>

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As noted, C++ is likely the way to go.

Before C++, the X guys did it with #includes of common code.

Similar could be done with macros,

but macros expand into a single line.

Any errors can be hard to locate.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It was when #1 said:

   // derived members

} Derived_t;

that the rather obvious "king of the derived" idea struck me.

 

C++ is all about inheriting/deriving from bases such as

} Base_t;

As seen the syntax is much easier to work with - not relying of clunky language extensions and

struct tactic : mob_type

is where the derivation actually happens.

 

Oh and nothing says you have to use any other feature of C++. Just put the code in a .cpp rather than .c and it will be passed to the C++ not the C compiler. Be warned that C++ is much more picky about assignments between differing types so it may call you out on such things (which is actually another "good thing" in fact)

Last Edited: Tue. Jun 12, 2018 - 04:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

MalphasWats wrote:
it throws about 57 errors that all point in all sorts of directions

Go on: just look at the first reported error first - almost certainly, that will be the cause of the other 56.

 

Exactly like Python gives you a  (possibly long)  trace-back of all the files through which an exception was propagated.

 

You'd think that, but it doesn't - having spent a bit more time thinking about it, I think I understand why it gives the error it does where it does, but it was very unhelpful.

 

initialisation makes integer from pointer without a cast

in glyphs.h (not touched by any of my changes):

#define PLAYER_OFFSET 60

scrolling through the other errors makes it obvious that the type wasn't created as I expected it to be, but the first few errors were very unhelpful!

 

EDIT: I take it back - that was the first error listed in the Errors pane in AS7, if I look at the actual 'Output' pane, the first error is:

 


C:\Git\Mage\AS7\mobs.h(34,17): warning: declaration does not declare anything
    struct mob_type;

Which is exactly where the problem is. ;)

Last Edited: Wed. Jun 13, 2018 - 09:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Another slightly off the wall "solution":

 

 

Thank you, I like the neatness of this solution. I figured it would be fairly straightforward in C++ but was avoiding using it because I don't even like using C, except that I'm almost starting to enjoy the simplicity of it working on this project and I didn't want to fall into a rabbit hole of learning another language poorly before I've even developed a single useful thing. I keep wishing there was a single language I could use for everything - little desktop apps, websites and electronics gizmos. But it's probably C or C++ (well, actually it's javascript, but I don't want to start getting offensive) and I've wasted the last 5 years or so getting half decent with something else :D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MalphasWats wrote:
that was the first error listed in the Errors pane in AS7, if I look at the actual 'Output' pane...

This is a frequent gripe about the 'Problems' list - it re-orders the messages.

 

angry

 

Always check in the 'Output' pane to see the errors in their proper order and context.

 

clawson frequently rants about this.

 

And I frequently note that Eclipse has exactly the same stupidity.

 

#StupidProblemsView  #UseTheOutputPane

 

 

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Wed. Jun 13, 2018 - 09:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MalphasWats wrote:
a single language I could use for everything - little desktop apps, websites and electronics gizmos
Personally I think C++ and Python together cover pretty much everything you might want to do. Remember that when it comes to the "electronics gizmos" you can just write in the C subset of C++ if that's what floats your boat. But there's some of the simpler features of C++ that make it look attractive even in a limited resource environment (also there's a lot of FUD about C++ being "resource hungry" that simply is not true anyway). Talking of some of the nice things that C++ might bring (apart from what I showed in #7). Just consider for a moment what's going on in something like Arduino's Serial.println(). That shows the use of methods in classes and also function overloading (it can take char, int, float, string, etc and print any of them). But you don't have to use this stuff if you don't want.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Depends what you mean by, "electronics gizmos".

 

There are viable implementations of Python on Cortex-M ...

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

when it comes to the "electronics gizmos" you can just write in the C subset of C++ if that's what floats your boat. But there's some of the simpler features of C++ that make it look attractive even in a limited resource environment.

 

I think I'm coming around to this - my main programming education started with Java at university, and whilst I never got on with the language itself, its object oriented nature stuck with me and settled nicely into Python later. I've always been wary of C++ because I'd been given the impression that its 'OOness' was just an ugly bolt-on to C and it's a bit 'unfashionable' what with all the new shiny languages that keep coming out. But if you take the time to think about it - there must be a reason it's still around and well used.

 

This little project I'm working on is supposed to be more about learning the hardware side of things/designing PCBs and things like that, but I ended up getting really over ambitious with the software. My intention is to write some different games for it in time, so I will definitely look at porting some of the more obvious bits of it to C++. There seems little point in trying so hard to force C to do things that C++ was designed to do in the first place!

 

Thank you for your input.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

Depends what you mean by, "electronics gizmos".

 

There are viable implementations of Python on Cortex-M ...

 

 

 

Indeed - I've got a box full of BBC Micro:bits sat on my desk that can all run Python (and JavaScript!). I'm stuck at a point of knowing just enough to be dangerous when it comes to electronics. I've had the idea for a miniature gaming device in my head for ages - I know there's loads of these sorts of things around, but I wanted to make my own. I also had a few bare ATTiny85 chips laying around and I liked the idea of pushing one as far as I could. I think I've done quite well so far. Now I'm getting frustrated because I've done so much with it, but can't really take it any further - I want to implement random map generation, a magic system, DIALOGUE! but I've hit the end of what I can reasonably do with 8K Flash and 512B RAM (although I wonder how feasible it would be to load stuff off an SD card or similar, not enough pins!).

 

I looked into moving to an ARM based chip, with the hope I could use micro python on it eventually, but that opens up a whole new set of problems with soldering tiny parts and not having the right programming hardware. I can buy a development board for one, but then I've gone backwards because I don't have a neat little PCB that I designed.

 

I also bought some ATTiny1614s which have a lot more SRAM and double the flash - I even did a pretty good job of soldering it onto a little breakout board so I can breadboard with it. But I didn't realise at the time that the 1-series tinies aren't really tinies, they're xmega and I don't know how to program them with my set up, so that's a whole other rabbit hole to chase down!

 

Actually, my very first attempt was Python running on a raspberry pi zero which I was intending to solder back-to-back to a custom board - https://twitter.com/Pi0CKET, but that didn't end (start) well!

 

I'm getting there, and I'm really grateful for the help I've received here. I quite often try to run before I can walk with things like this and some early successes might have made things look easier than they really were!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

MalphasWats wrote:
that the 1-series tinies aren't really tinies, they're xmega and I don't know how to program them with my set up
If you want something "familiar" with a lot of resources consider mega1284. Sure it's got 40 pins (so perhaps 30+ more than you really want) but the point is that it has more RAM than any other Mega AVR at 16K (and 128K flash is pretty healthy too). If you can get the solution into fewer resources you can then scale back through 644, 324 to 164.