C typedef enum - what am I doing wrong here? - Page 1
Forum section: electronics / general

author: HwAoRrDk
author_rank: Super Contributor
timestamp: December 15, 2018, 08:42:09 pm
content: I feel like I am going crazy. I've been banging my head against this for nearly an hour now. I'm 99.9% sure this should be working. What's going wrong here?I have two header files. In one I have an enum declared, and in the other I have a struct that has a member of that enum type.Code:/* foo.h */#ifndef FOO_H_#define FOO_H_#include "bar.h"typedef enum {XXX = 0,YYY = 1,ZZZ = 2} foo_enum_t;#endifCode:/* bar.h */#ifndef BAR_H_#define BAR_H_#include "foo.h"typedef struct {/* other members */foo_enum_t foo;/* etc */} bar_t;#endifAnd yet, when I compile, I'm getting the error "unknown type name 'foo_enum_t'" at the line in the struct in bar.h where I reference the enum type.I don't believe I need to 'extern' my enum's typedef - in fact I don't think you even can. Is it maybe something getting confused by the recursive include of each header file within each other (i.e. foo.h includes bar.h, which includes foo.h...), but the include guards should avoid any trouble from that, right? If I include foo.h in some other different header file, the enum is known there and there are no compile errors. But these two headersneedto include each other as foo's functions use the bar_t struct type.

author: paf
author_rank: Regular Contributor
timestamp: December 15, 2018, 08:46:14 pm
content: You are including "bar.h" in "foo.h".Not needed.

author: MosherIV
author_rank: Super Contributor
timestamp: December 15, 2018, 08:49:35 pm
content: QuoteNope. The include guard simply stops the same definitions from being re-defined if they have been defined before from the same header.Whenever you do a #include the compiler will always load and paste the entire header file into the file being compiled.

author: tsman
author_rank: Frequent Contributor
timestamp: December 15, 2018, 08:53:18 pm
content: You included foo.h which sets FOO_H_ then includes bar.h which tries to include foo.h again but FOO_H_ is already set so does nothing. It continues parsing bar.h and now comes across foo_enum_t which it has no clue about so gives that error. It is only after it has finished parsing bar.h that it continues with the rest of foo.h and sees the enum but it has already failed with an error by this point.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: December 15, 2018, 08:56:04 pm
content: Really? I couldswearI've done this kind of thing before without any problems. Maybe the mind is playing tricks on me...It's going to be a pain if I have to, say, make a third include file and put both enum and struct into that, as that'll be blurring functionality and logical boundaries. My code is all so nicely organised at the moment.

author: rstofer
author_rank: Super Contributor
timestamp: December 15, 2018, 10:08:45 pm
content: You didn't show main.c so who knows which order the includes are in. If, in main.c, you include only bar.h, the code should compile. I think...When bar.h is expanded, it will include foo.h which will try to include bar.h but that's already blocked so foo.h will create the typedef and bar.h should use it.Code:#include "stdafx.h"#include "bar.h"bar_t FooBar;int main(){return 0;}This seems to compile...

author: Nusa
author_rank: Super Contributor
timestamp: December 16, 2018, 01:49:26 am
content: Quote from: HwAoRrDk on December 15, 2018, 08:42:09 pmNo, this is where you went wrong. bar.hneedsfoo.h, but foo.h has no need of bar.h. So why include bar.h in foo.h? Especially since it fails for obvious reasons if bar.h ends up being declared prior to foo.h.The value of include guards is so that multiple references to the same include file pass muster. e.g. if bar.h includes foo.h, and squared.h also includes foo.h, you don't have a problem including both bar.h and squared.h.Recursivereferences are an usually only a step away from total failure, even when you get away with it. Never do that on purpose!

author: IanB
author_rank: Super Contributor
timestamp: December 16, 2018, 02:32:57 am
content: Quote from: HwAoRrDk on December 15, 2018, 08:42:09 pmThis is always a bad idea, and will always need to trouble. A better description of "recursive include" is "circular dependency". You do not want circular dependencies, ever. If you have a circular dependency you have done something wrong.

author: richardman
author_rank: Frequent Contributor
timestamp: December 17, 2018, 02:47:46 am
content: As @rstofer said: the key to your problem is whether you are including foo.h or bar.h in your .c file.If you include bar.h, your file should compile OKIf you include foo.h, you will get exactly the problem you describe.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: December 17, 2018, 07:12:24 pm
content: I managed to resolve the issue, by re-evaluating how in general I was handling all my included headers across the project.Basically, I realised I would be better off not including things in the .h files of a module that aren't actually necessary in facilitating the inclusion of that .h in another module - rather, I should split the things that are included between a module's .h and .c. The .h willonlyinclude the things that are required to express function definitions, struct, enums, etc (e.g. stdbool, stdint). Anything else: in the .c.Doing this meant that the inclusion of bar.h was moved to foo.c, and so - as I suspected and was rightly pointed - a circular dependency was avoided.Thanks for the help everyone.

author: richardman
author_rank: Frequent Contributor
timestamp: December 17, 2018, 08:35:08 pm
content: Quote from: HwAoRrDk on December 17, 2018, 07:12:24 pmSorry - you really need to understand why it was a problem in the first place. It was NOT a problem with circular dependency, but misunderstanding of... a bunch of things.