Jens Gustedt's Blog

April 20, 2012

struct tags in C++ are even weirder

Filed under: C++, C11, C99 — Jens Gustedt @ 22:14

I already discussed that fact that struct tags are not identifiers in C++, and in particular that a tag name can be used as the name of a types. Today I learned that the rules for that are even more complicated than I thought. In C an identifier that has been used as a tag (for struct, union or enum) can freely be used as another identifier (variable, typedef, label). In C++ only almost: there is one sort of identifiers it can’t be used for, typedef unless it refers to the same type as the tag.

So the rules in C++ are somehow

  • struct toto; declares a type that can be accessed through the name toto (without struct keyword) if this is unambiguous.
  • It can also be used to declare another kind of object, and then to get access to the type we’d have to prefix the name with the keyword struct.
  • The other kind of object may be anything, but it can’t be a type…
  • … unless it refers to exactly the same type as before.

This is the standard example where everything works fine, a tag name an a typedef with the same identifier clean that refers to the same type:

struct clean {
  unsigned a;
};
// A typedef that binds the same type to the name clean as well
// legal for C
// legal for C++
typedef struct clean clean;

If we don’t have the typedef, in C we can’t use the tag name as a type without the prefix struct:

struct C {
  unsigned a;
};
// A function that returns the struct we just defined
// legal for C++
// illegal for C, we need the struct before the first "C"
C C(void);

But if we define a new type with the tag name, for C++ the type has to be the same and the following (awful) hack doesn’t work:

struct CPP {
  unsigned a;
};
// A typedef that binds a different type to the name CPP
// legal for C
// illegal for C++, refers to a different type
typedef struct CPP * CPP;

So if we put the last two blobs together in one source file, we have something that doesn’t compile, neither for C or C++. But for completely different reasons. One errors out on the “C” line, the other one on the “CPP” line.

My personal taste tends to converge towards finding the three-fold exception list of C++ just ugly. For me this clearly shows the tendency of complisficating the language. With all the best intentions…

Advertisements

4 Comments

  1. I first saw the idiom:

    typedef struct Node Node;
    struct Type {
    int val;
    Node *prev;
    Node *next;
    };

    in Plan 9 code. It works well both for C/C++ compatibility and for insuring you don’t run into forward declaration issues if you like calling struct tags without using the struct keyword. The only place it can bite you is if you have mutually referential structures, but I’m pretty sure you have to forward declare C++ structs/classes in such situations as well.

    As for the final example, I think it’s best not to hide pointers behind a typedef in most every situation. Code like

    typedef struct Node *List;

    is just begging for trouble. It’s best to be transparent with what’s an object and what’s a pointer to an object.

    BTW, you have some HTML embedded in your code. Makes it hard to grok.

    Comment by pkmaysatar — May 2, 2012 @ 15:07

    • The only place it can bite you is if you have mutually referential structures, but I’m pretty sure you have to forward declare C++ structs/classes in such situations as well.

      No, why should it? Just put the two typedefs in front:

      typedef struct Node Node;
      typedef struct Edge Edge;
      

      BTW, you have some HTML embedded in your code. Makes it hard to grok.

      Can you elaborate on this? Here on this blog this is primarily the other way round, code embedded in HTML, no?

      Comment by Jens Gustedt — May 2, 2012 @ 15:29

      • Yes, that’s what I meant, I didn’t word it as well as you did though.

        I’m pretty sure that some HTML leaked into your C code. Here are the offending lines:

        // illegal for C, we need the struct before the first <span class="hiddenGrammarError" pre="">C
        C</span> C(void);
        

        It should probably read:

        // illegal for C, we need the struct before the first C
        C C(void);
        

        Comment by pkmays — May 2, 2012 @ 15:44

        • argh, yes now I see it, thanks!
          corrected, after struggling much with the autocorrection feature of the WP site

          Comment by Jens Gustedt — May 2, 2012 @ 15:55


RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Blog at WordPress.com.