1

Our universitie's homework submission system has - instead of focusing on programming skills - set ridiculous and impractical requirements on submissions. To bypass that, I use preprocesor and few other tricks to merge my homework solution into one file (one of the requirements).

Another requirements is that no warnings must occur - and -Wpedantic is enabled. I forward-declare a struct in node.h so that I can use it in function calls:

typedef struct Edge Edge;
// So that I can do:
typedef struct Node {
    void* value;
    int name;
    Array* edges;
} Node;
Edge* node_find_edge(Node* node, NodeName target);

In a different file - edge.h - full definition reads as:

typedef struct Edge {
    size_t cost;
    NodeName A;
    NodeName B;
} Edge;

I get this warning:

main.c: At top level:
main.c:779:3: warning: redefinition of typedef 'Edge' [-Wpedantic]
 } Edge;
   ^
main.c:753:21: note: previous declaration of 'Edge' was here
 typedef struct Edge Edge;

Don't get confused by the "main.c" thing, that simple because the files are merged as I said.

What forward declaration is correct and warningless?

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • Note that I have workarounds around the issue, but the question stands - how to properly use forward declaration! – Tomáš Zato Jan 10 '17 at 01:32
  • Why can't you just exclude that line? `typedef struct Edge Edge;` or replace it with the full definition – Gabriel Pellegrino Jan 10 '17 at 01:37
  • @GabrielPellegrino I don't include `edge.h` in `node.h`. Instead, I use forward declaration. This originally had a very good reason - and makes very good sense in different application. – Tomáš Zato Jan 10 '17 at 01:38
  • 1
    A requirement for warning-free compilation is mostly a good thing -- in theory warnings indicate bugs (perhaps of a lesser severity than errors). – M.M Jan 10 '17 at 01:43
  • Note: The prohibition on re-defining a typedef to the same thing has been removed in C11. If you are allowed to use compilers that are less than 6 years old , that would be a solution – M.M Jan 10 '17 at 01:44
  • There doesn't seem to be any circular dependencies between the structure types. Why can't you just put the declaration of `Edge` before this code, so you don't need a forward declaration? – Barmar Jan 10 '17 at 01:52
  • 1
    Sidenote: the `*` for pointers belongs to the name, not the type. Trick question: "`int* p, q;`: what types are `p` and `q` and why?" So better write `int *p, q;` consistently. – too honest for this site Jan 10 '17 at 02:09
  • 1
    @Olaf That's a trick question indeed, pulled out by people as the one and only argument why put asterisk to the variable name. I think this answer wraps the whole issue nicely: http://stackoverflow.com/a/2660648/607407 – Tomáš Zato Jan 10 '17 at 02:15
  • @M.M It sounds like the homework submissions are compiled automatically, so he doesn't get to control which compiler is used. – Barmar Jan 10 '17 at 02:15
  • @TomášZato: And you can write a whole C program in one line. Which is the same argument as your's. Reason why we format code properly and even use comments from time to time (at least those who prefer to still understand the code one year later) is: readability and maintenability. If you read the C grammar, you might nottice the `*` is part of the declarator, not the type-specifier. It only does not matter for a single declarator for obvious reasons. So why not be consistent? (btw. I know of three coding styles which require that and none for the other). – too honest for this site Jan 10 '17 at 02:19
  • @TomášZato: It looks like the only reason to place the `*` next to the type is _"because prefers this"_. As a novice in the language, adding it next to the name seems more logical, as it targets the variable, not the whole declaration. – Cerbrus Jan 11 '17 at 07:34
  • @Cerbrus That's not the only reason, it's actually no reason at all. My point was that the answer I linked nicely describes how stupid and repetitive are the arguments in this whole debate. The actual reason people put star next to type is, of course, because that reads as "I'm declaring variable of type pointer to X". Also, it makes sense if you sometimes use just the type without the variable name, eg. `void func(int*);` – Tomáš Zato Jan 11 '17 at 11:40

1 Answers1

3

Don't use the typedef, just use struct Edge*

struct Edge; // forward declaration
struct Edge* node_find_edge(Node* node, NodeName target);
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • May as well suggest to use `struct Node` as well. That's another typedef the OP may trip on in the future. – StoryTeller - Unslander Monica Jan 10 '17 at 01:49
  • @StoryTeller There's no forward declaration for that, the structure declaration is right there. – Barmar Jan 10 '17 at 01:51
  • I even wrote explicitly that I also need/want to define the type. So that I can use `Edge`, not `struct Edge`. – Tomáš Zato Jan 10 '17 at 02:12
  • @TomášZato Where did you write that? – Barmar Jan 10 '17 at 02:13
  • The title says "struct AND type". Maybe I used wrong terminology. Honestly I still find the whole `struct Typename` thing confusing. Can't see the point, but I certainly want to avoid using `struct` before type name. – Tomáš Zato Jan 10 '17 at 02:16
  • Most of the time you can, except when you're doing forward declarations. And this was fixed in C11, as pointed out in the comments. C++ also fixed this, since it automatically creates type names from structure and class declarations. – Barmar Jan 10 '17 at 02:23
  • So as far as C99 is concerned, it is not possible? – Tomáš Zato Jan 10 '17 at 02:24
  • @TomášZato I suspect not. That's why it was changed in C11, so you wouldn't have this wart. – Barmar Jan 10 '17 at 02:25
  • 1
    @TomášZato perhaps you could arrange your headers so that `typedef struct Edge Edge;` is in its own header (with include guards), say `edge_fwd.h`, that is then included by `edge.h` and `node.h` – M.M Jan 10 '17 at 02:37
  • @Barmar - Forward declaration or not, it's another example over over-typedefing everything that less experienced programmers usually trip over eventually. Plus, if you use a typedef for one struct but not another, it breaks consistency, and that IMO is a code smell. – StoryTeller - Unslander Monica Jan 10 '17 at 06:40