MPLAB X/XC32, confusion over best style for struct definitions - Page 1
Forum section: electronics / general

author: Chris C
author_rank: Frequent Contributor
timestamp: August 25, 2015, 06:08:15 am
content: I have a structure defined in a header, serving as a node in a doubly-linked list. I'm in the habit of defining a struct like this, so that I don't have to prefix every reference to it with 'struct':STYLE #1:Code:typedef struct {DLISTNODE* Next;DLISTNODE* Prev;void* Data;} DLISTNODE;But the compiler doesn't accept that on this particular struct, having the unusual property of being self-referential. Ok, no problem, I can do this instead:STYLE #2:Code:typedef struct STRDLISTNODE DLISTNODE;struct STRDLISTNODE {DLISTNODE* Next;DLISTNODE* Prev;void* Data;};#define ZINLINE __attribute__((always_inline)) static __inline__ZINLINE DLISTNODE* DListNext(DLISTNODE* n) { return n->Next; }; // Gets next node.Now the compiler is happy, and the code runs correctly. But the IDE can't figure it out, and marks every reference to DLISTNODE members in red, like 'return n->Next'. Leaving my code littered with false errors, making it hard to pick out real errors, and disabling contextual suggestions when writing code using this structure.Looking up some example code for doubly-linked lists, I found another style, which I'd never seen before:STYLE #3:Code:typedef struct DLISTNODE {struct DLISTNODE * Next;struct DLISTNODE * Prev;void* Data;} DLISTNODE;Now the compiler is happy, and so is the IDE - but only with this particular structure. I have many other (non-self-referencing) structs defined in style #1, which the IDE previously had no issue with, and now it's failing on all of them! It's as if upon seeing the new style once, it's unable to handle any other style. I tried changing one struct definition to the new doubly-named style, and it no longer had issue with that struct.Leaving me wondering whether I should:1) Revert the self-referencing structure to style #2, and let the IDE complain about just that one.2) Convert all my struct definitions to style #3 (this will take a while in 20K lines of code).Before considering #2, I need to fully understand this new doubly-named style. It seems that it creates overlapping names in the namespace. One being a struct, one being a typedef, and both with the same name. That's normally something I'd avoid, even if allowed, as it introduces ambiguity. Yet I found someone saying it's the "common idiom", and if so, I'd consider that a valid reason to convert:http://stackoverflow.com/questions/1675351/typedef-struct-vs-struct-definitionsBut is that really true? Or are there downsides to this? Even if it works on this compiler/IDE, if I were to migrate this code to another C compiler not based on GCC (like Visual C), or perhaps C++, would it still work?

author: BloodyCactus
author_rank: Frequent Contributor
timestamp: August 25, 2015, 12:03:01 pm
content: #3 is the correct way to do it. #1 is wrong, since your referencing something that does not exist yet. you should convert all to #3 and save yourself hassles going forward.you could import the code into eclipse/netbeans and see if it will refactor it for you or do it by hand or write a ruby script or something to do it for you.but type #3 is what you should have been using all along. this is not a new thing in the world of C, this was the rule from way back.

author: Chris C
author_rank: Frequent Contributor
timestamp: August 25, 2015, 06:34:24 pm
content: It's hard to tell what's a "rule" in C.For example, I Googled "C typedef". Every link on the first page of search results was relevant, and I examined them all (including the Wikipedia page). Style #1 and #2 were represented. Style #3 was NOT. At least not with the struct and typedef names the same, though it was represented with these names being different; which should really be called:STYLE #4:Code:typedef struct STRDLISTNODE {struct STRDLISTNODE * Next;struct STRDLISTNODE * Prev;void* Data;} DLISTNODE;I picked style #1 as my standard because it's cleanest, shortest, easiest to type, and worked until now. Style #3 would be my second choice, but this really is my first time seeing it! And if there are circumstances where having both names the same may cause issue in the future, then style #4 would be better, although it's my third choice.But that's one vote for style #3, and thank you for it. Do I hear a second, just to be sure?

author: BloodyCactus
author_rank: Frequent Contributor
timestamp: August 25, 2015, 07:06:07 pm
content: Now I look at it, yes I use different names, so #4 would be the closest to the way I do it, I always create mine thusly if it has to be self referential.Code:typedef struct udtSomething{struct udtSomething *next;} uSomething, *pSomething;

author: Chris C
author_rank: Frequent Contributor
timestamp: August 25, 2015, 07:38:19 pm
content: '*pSomething'? If I'm parsing that correctly, that allows you to later write 'pSomething' instead of '*uSomething'. But I'm having a hard time imagining how that might be advantageous. Why do you use it?

author: Maxlor
author_rank: Frequent Contributor
timestamp: August 25, 2015, 07:49:54 pm
content: I recommend style #4 too. This naming convention is in common use on many unix systems:Code:typedef struct _mystructname {...} mystructname;Quote from: Chris C on August 25, 2015, 07:38:19 pmIt's syntactic sugar. There's no real advantage, i.e. there's nothing you can do with it that you can't do without it. It lets you avoid things like having ** appear in your code which I guess some people don't like. Personally, I'm not a fan of that kind of typedef, just give me the standard language, thanks.

author: Chris C
author_rank: Frequent Contributor
timestamp: August 25, 2015, 08:49:53 pm
content: Quote from: Maxlor on August 25, 2015, 07:49:54 pmI don't like pointers-to-pointers either, but only because I tend to make mistakes when using them. I'm inclined to keep using **, I've grown accustomed to it serving as an easily recognizable big red flag regardless of datatype, telling me I need to slow down and pay attention.And with the second vote for #4, I think I can call this issue closed. Thank you both!

author: BloodyCactus
author_rank: Frequent Contributor
timestamp: August 25, 2015, 09:38:03 pm
content: Quote from: Chris C on August 25, 2015, 07:38:19 pmits just something I've been doing over the years for a long time. Been writing C since pre ansi days, I guess we all have our style likes/dislikes. I would agree not abstracting/typedefing away **.

author: Chris C
author_rank: Frequent Contributor
timestamp: August 25, 2015, 11:26:52 pm
content: Just a quick follow-up for anyone who might find this thread useful in the future.I have successfully converted most of my struct definitions to style #4. Two that were challenging, because they reference each other. The following (stripped of non-important members) seems to work, as far as compiler/IDE goes; though I haven't yet tested the portion of code using these structs, I don't think there will be any issue:Code:typedef struct STRTASKINFO TASKINFO_PREDEF;typedef struct STRRESOURCEINFO {TASKINFO_PREDEF* LockedBy;void (*ResourceFunc)(struct STRRESOURCEINFO* Resource, TASKINFO_PREDEF* Task);} RESOURCEINFO;typedef struct STRTASKINFO {RESOURCEINFO* AutoResource;} TASKINFO;Also, it seems that once I converted all struct definitions in header files to style #4, the IDE stopped complaining about the few remaining structs in source files still defined using other styles! I don't know why. I'm not going to give it too much thought, I need to get back to productive coding.

author: dgtl
author_rank: Regular Contributor
timestamp: August 26, 2015, 05:19:00 pm
content: Haven't used MPLAB, but generally in C you may want to use forward declaration in that case:Code:struct STRTASKINFO;typedef struct STRRESOURCEINFO {struct STRTASKINFO* LockedBy;void (*ResourceFunc)(struct STRRESOURCEINFO* Resource, struct STRTASKINFO* Task);} RESOURCEINFO;typedef struct STRTASKINFO {RESOURCEINFO* AutoResource;} TASKINFO;This mixed use of typedef-ed and non-typedef-ed types creates a mess. Because of that, I rather side with Linux kernel coding style, that prohibits the use of typedefs by default (for details read chapter 5 athttps://www.kernel.org/doc/Documentation/CodingStyle).

author: Chris C
author_rank: Frequent Contributor
timestamp: August 26, 2015, 06:55:44 pm
content: Quote from: dgtl on August 26, 2015, 05:19:00 pmThat's nicer. My brain was pretty fried when I wrote that. Thanks!Quote from: dgtl on August 26, 2015, 05:19:00 pmI came across that chapter, and read it, yesterday while researching this issue.Excluding uses which that document considers acceptable, I use typedef almost exclusively to eliminate the need to type struct or union each time one is referenced. I can see why the given example of:vps_t a;Could be confusing instead of:struct virtual_container *a;Because it hides the fact that it's a pointer, as well as being less descriptive. But:virtual_container *a;Seems fine to me. If you're writing new code using a virtual_container, you know what a virtual_container is. If you're looking at old code using virtual_container, a cursory examination of the code reveals that it's a struct or a union, without having it explicitly spelled out. And with properly written code, it doesn't even seem necessary to know specifically whether it's a struct, or a union; any more than you would need to understand the internal data storage structures of a class in an OOP language to use it.Maybe for the Linux kernel, where you presumably have dozens of people trying to maintain 10M lines of code, a highly restricted code style is advantageous; by leaving nothing - even what should be obvious - to chance. Or to avoid mixing of styles, if some people typedef away the need for struct/union and some don't, then that would indeed be a mess. But I typedef those away with 100% consistency so no mess, and I'm just one person with 20K lines of code. It's more important to me to keep a good balance between code that is short, yet understandable; and I feel explicitly writing struct/union adds length with questionable contribution to understandability.Though if there are any aspects of this I've failed to consider, I'd be interested to hear about them.

author: kc8apf
author_rank: Regular Contributor
timestamp: August 27, 2015, 04:24:15 am
content: Not using typedef removes the ambiguity of whether 'struct' is needed in a given context. C++ fixed this by making structs a first-class type.

author: helius
author_rank: Super Contributor
timestamp: August 27, 2015, 09:29:18 am
content: Please do not use google (or heaven forfend, wikipedia) to learn basic programming techniquesInstead do yourself a favor and read a book. In the excellent "C: A Reference Manual", you can learn all the rules for type declarations, including the special rules for self-referential structs.You would also be well advised to pick a style for identifiers that removes ambiguity. ALLCAPS is most often reserved for macros. typedef'd names are usually affixed with _t. names in the struct space are sometimes affixed with _s, although this is less common.

author: Chris C
author_rank: Frequent Contributor
timestamp: August 27, 2015, 04:41:34 pm
content: Quote from: helius on August 27, 2015, 09:29:18 amI did. And at that time, about 20 years ago, I knew all this like the back of my hand. Then 10 years passed without using C, before starting this project. I can still code, but some occasionally find some detail has disappeared from my memory. Like the topic of this post. Or who I lent that book to, that never returned it.A book only exposes you to one style anyway. Which may be a good style, but seems few will agree that it's the best, at least unless you change this, and that... I just try to be consistent, and I find that easiest to do that when the style is intuitive. If a macro looks and acts like a constant, it's named like A_CONSTANT, so I know it's not a variable. If a macro looks and acts like a function, then I don't care while using it whether it's a macro or a function, so it's named like AnyOtherFunction.

author: jnz
author_rank: Frequent Contributor
timestamp: August 27, 2015, 05:00:19 pm
content: Yea.... well.... you know who uses Style1? Everyone else.

author: mikerj
author_rank: Super Contributor
timestamp: August 27, 2015, 05:19:33 pm
content: Quote from: helius on August 27, 2015, 09:29:18 amPlease don't make the mistake that a single book can teach you everything you may ever need to know about programming, and that every resource on the internet is useless

author: ez24
author_rank: Super Contributor
timestamp: August 31, 2015, 08:00:36 pm
content: helius this is Chris C drilling on a boardhttps://youtu.be/BGOUSvaQcBs?t=108Seriously Chris needs to write a book not read one, I think he spends too much time proofreading PIC datasheets.Chris what is your project? Some sort of home automation, water treatment? Are you going to write a paper on it?I looked through this to see if maybe you are in this :BUT this is 8 hours long !

author: Chris C
author_rank: Frequent Contributor
timestamp: September 01, 2015, 05:48:32 am
content: Yep, it's home automation.

author: ez24
author_rank: Super Contributor
timestamp: September 01, 2015, 11:05:23 pm
content: Quote from: Chris C on September 01, 2015, 05:48:32 amThe first time I thought about a robotic house, that's why I found the DARPA robots. I was thinking of a house with legs. But now I wonder what could be done with a PIC32. My guess more than dimming lightsCan I ask what type of automation you will be doing?thanks

author: Chris C
author_rank: Frequent Contributor
timestamp: September 04, 2015, 06:03:04 pm
content: Lights, remote sensors, and quite a few aquariums. Nodes on the cars, providing remote battery voltage sensing, to let me know if I'm draining the battery because I left a dome light on or whatnot. Stuff like that.It's already very functional, but not done, because I am enjoying programming MCUs. A lot. And I just can't help myself from implementing any interesting idea that comes to mind. I have a post here that describes what I'm currently working on:http://hackaday.com/2015/09/04/embed-with-elliot-practical-state-machines/#comment-2702404

author: ez24
author_rank: Super Contributor
timestamp: September 05, 2015, 07:25:39 am
content: QuoteI think this is a good idea. Currently I am stuck at home, neither my car nor truck would start today. The car needs a new battery and my truck needs a new starter. It would be nice to see the voltages inside the house.