How to initialise an array of structs which contains an array


(Matthew Vicaradge) #1

Hi again,

I was wondering if someone can help me with an issue I am having? I have a struct that contains a 2d array. I need an array of the structs but I cant initialise the array in the struct without getting an error “room does not name a type”.

typedef struct{
  int RoomNum;
  int RoomType;
  int Exits[4][3]; //Direction,Active,which number the room links to,
}Room;

Room room[3];

room[0].RoomNum = 1;
room[0].RoomType = 1;
room[0].Exits[4][3] = {{1,1,2},{2,1,3},{3,0,0},{4,0,0}};

As always any help would be greatly appreciated.

Thanks,


(Pharap) #2

Firstly, this is C++, you don’t need to typedef your structs, that’s a C-ism.

struct Room
{
	int RoomNum;
	int RoomType;
	int Exits[4][3];
};

Secondly, you can’t initialise a global array by indexing the items, you have to initialise all the items at once:

struct Room
{
	int RoomNumber;
	int RoomType;
	int Exits[4][3];
};

Room rooms[3] =
{
	// Room 0
	{
		// RoomNumber, RoomType,
		1, 1,
		// Exits
		{
			{ 1, 1, 2 },
			{ 2, 1, 3 },
			{ 3, 0, 0 },
			{ 4, 0, 0 },
		}
	},
	// Room 1
	// etc ...
};

Lastly, you might not have learnt some of this yet, but ideally your rooms should be structured like this:

enum class Direction : uint8_t
{
	North, East, South, West,
};

struct Exit
{
	Direction direction;
	bool isActive;
	uint8_t roomNumber;
};

enum class RoomType : uint8_t
{
	Normal,
	// Other room types
};

struct Room
{
	uint8_t roomNumber;
	RoomType type;
	Exit exits[4];
};

Room rooms[3] =
{
	// Room 0
	{
		// roomNumber
		1,
		// type
		RoomType::Normal,
		// exits
		{
			{ Direction::North, true, 2 },
			{ Direction::East, true, 3 },
			{ Direction::South, false, 0 },
			{ Direction::West, false, 0 },
		}
	},
	// Room 1
	// etc ...
};

uint8_t is 1 byte instead of 2 bytes, so if you don’t need the 2 bytes worth of values then it’s a big memory saver.
uint8_t is also ‘unsigned’ which means it doesn’t represent negative numbers.
uint8_t has a range of 0 to 255, versus int8_t's -128 to 127 and int16_t's -32768 to 32767 (on Arduboy, int is the same size as int16_t, but that isn’t true on all platforms).

Whenever you have a ‘yes or no’ condition like isActive,
it should be a bool with true and false, not an int with 1 and 0,
and whenever you have a series of related named values,
you should use an enum class (technical named ‘scoped enumerations’),
they’re more descriptive than bare numbers and they’re a lot safer too.


(Matthew Vicaradge) #3

Thats awesome, thanks. I’ll try it out today!


(Jean Charles Lebeau) #4

Pharap you’e eally great to learn how to program. You should write some workshop on Gamebuino Meta Academy… You could make so cool tutos… Else permit me to use all you make as solution or tips to compile them in Gamebuino’s academy… All this have to be kept (but i’ll write the workshop in french as i have a poor english level)


(Pharap) #5

I consider any advice that I post on any forum (be it Arduboy or Pokitto) to be free for use,
so you’re free to use it to write tutorials or whatever.

If you do use something that I’ve said then I’d prefer to be cited/mentioned if possible,
but it’s not strictly necessary, I won’t complain if you forget to mention me.
(I will complain if you don’t link to cppreference though. :P)

I’d honestly like to write some tutorials some time,
but every time I start trying to write one I get stuck, or bored,
or something else comes up that needs my attention more than the tutorial.

If you do compile the tips,
I’d suggest putting them on your GitHub account as well (assuming you have one),
preferably in Markdown (.md) format, so you can link to them easily from other forums.


(Jean Charles Lebeau) #6

Thanks Pharap. I’ll write on Gamebuino site as we try to organize an academy with a logical progession to make games. All your advices will have their place and ill try to compile your tips in standalone tuto by themes to keep them and reference them then they are enough long. So i refer you as Pharap or you prefer another name ?


(Pharap) #7

Pharap’s the only name the internet knows me by.

I don’t really have a website, so if you need to link to something,
link to my GitHub, my Arduboy profile and/or my Pokitto profile.


If you need any more advice to include, check the isocpp FAQ (its recommendations are why I stopped using (void) to mean ‘no arguments’) and the isocpp core guidelines (still a work in progress).
There’s also some useful stuff on Bjarne Stroustrup’s website.


Lastly, I actually started writing a style guide recently, so that might be useful too.
(Also I’d really love to convert everyone to Allman brace style.)


If you need anything else, it’s probably best to send me a PM so we don’t drag this thread too far off topic.


(Simon) #8

Never going to happen :grin:


(Pharap) #9

I can dream, can’t I?


#10

Why do you prefer that over

void function (){
    //functioning
}

?


(Matt) #11

To make the code a bit easier to read, you should be able to do this

Room rooms[3] = {
	// Room 0
	{
		.RoomNum = 1,
        .RoomType = 1,
		.Exits = {
			{ 1, 1, 2 },
			{ 2, 1, 3 },
			{ 3, 0, 0 },
			{ 4, 0, 0 },
		}
	},
	// Room 1
	// etc ...
};

oh hell no :slight_smile:


(Matt) #12

And also, this style of syntax works

Room room = {
  RoomNum: 1,
  RoomType: 1,
 ...
}

You have to initialize the values in the order they are defined in the struct, despite naming them. You’d think the compiler could figure it out but it looks like gcc gave up on implementing that, as you get this error: sorry, unimplemented: non-trivial designated initializers not supported


(Pharap) #13

Because then you can just glance at the code and immediately see the brace pairs:
BracePairs

Which makes it easier to identify scope blocks:
BlockScope

(I didn’t write this code, I’m just using it as an example, as the spelling of catalogue should tell you.)

Actually the .RoomNum = syntax is not standard C++, it’s a compiler extension.
It is slated to be added to the standard in C++20,
but until 2020 it’s officially non-standard.

(Also ‘easier to read’ is very much debatable.)

Again, non-standard, except in this case it’s not even slated for standardisation,
so this will only be a compiler extension for the forseeable future.


(Matt) #14

uh oh code formatting war incoming, time to run.

On my way out, spaces are better than tabs! :stuck_out_tongue:


(Matt) #15

Being nonstandard doesn’t bug me. If it works with my compiler, I’m fine.

As for readable, you don’t have to rely on comments. Comments get out of date and can be wrong. The compiler can enforce aspects of the definition where comments can’t enforce anything.


(Pharap) #16

That’s not going to fly if you ever have to write multiplatform code.

I was talking about the braces.

Only if you’re not in the habit of updating comments at the same time that you update the code.

Fortunately the Arduboy2 library doesn’t seem to have a problem managing this.

Either way you have to be disciplined.
The compiler will enforce the rules, but it won’t stop you abusing the rules or trying to circumvent them.


(Matt) #17

We’re just humans, we will forget sometimes. Especially on large teams. In large code bases at work I basically don’t trust comments at all. The compiler never forgets.

That’s fair, it’s all trade offs. I’m not sure I’d ever port to a platform that didn’t use GCC, especially for arduboy games. And if I did, converting back is a little annoying but not terrible.


(Pharap) #18

The best way to prevent forgetting is to get into the habit of always checking.
Many things in programming are governed by habit and familiarity, like code style and architecture.

Having teams should make it easier to spot discrepencies by forcing code to be reviewed before it’s accepted.

Writing code for a company is a different environment to writing code for enjoyment.

There’s more timescales and deadlines, so things are more likely to get missed anyway versus having the time to review the code at leisure.

Often code will be ‘good enough’ rather than ‘exceptional’ because employers care more about code doing its job and not having bugs than they do about its quality.

If you write standard-compliant code then your code will work on any compiler that correctly implements the standard.

If you rely on compiler extensions then you force your users to use the compiler you want to use, thus alienating potential users, just like writing OS-specific code.

It’s easier to just get used to writing standard-compliant code and then never having to worry.


(Matt) #19

The best way to prevent forgetting is to have a computer automatically check. This is why tools like linters, formatters, strongly typed languages, and testing frameworks exist. Anything you can offload to automation, you should. You’re a fan of enum classes over plain enums for example. Just apply that type of thinking more broadly.

Sure, but teammates are still humans and still make mistakes.


(Simon) #20

Exceptional and quality? Interesting words …

Code ‘doing its job and not having bugs’ should be the highest of priorities. Using techniques that are ‘exceptional’ can result in the code only being maintained by the best members of the team an leaving the lower members behind. This is particularly an issue if the team members change (contractors) and you cannot rely on super humans each time.

After ‘doing its job and not having bugs’, performance is usually the next factor to consider.

The average project manager is not going to fund a developer how insists on hand-crafting each block of code. It is always a compromise.