Search code examples
ctypedef

How to define a typedef struct inside another one with anonymous union?


I want to represent a nested ast node

typedef struct ast_assignment
{
    char* sym;
    ast_node* value;
} ast_assignment;

typedef struct ast_conjunction
{
    char* sym;
    ast_node* value;
} ast_conjunction;

typedef struct ast_node {
  int node_type;
  union {
    ast_assignment data;
    ast_conjunction data;
  };
} ast_node;

but no matter what I define first, the compiler tells me that the other struct is an unknown type? How to proceed?


Solution

    • Typedef has to describe an actual type name
    • Typedefs require a ; after them
    • Circular type references require a bit of special sauce
    • Unions require different member names

    Might you mean:

    struct ast_node; // forward declaration
    
    typedef struct {
        char* sym;
        struct ast_node* value;
    } ast_assignment;
    
    typedef struct ast_conjunction {
        char  * sym;
        struct ast_node* value;
    } ast_conjunction;
    
    typedef struct ast_node {
        int node_type;
        union {
            ast_assignment  data_asg;
            ast_conjunction data_conj;
        };
    } ast_node;
    

    Expanding on this a bit for a more complete answer.

    First, the purpose of a typedef is to install a new name into the compiler that you can use, so typedef struct { ... }; doesn't really do much. It should be typedef struct {...} newtype; so you can use that actual name newtype.

    It also requires a semicolon after, which messes me up every time I go back from C# to C :-)

    The circular reference thing is tricky, and doing struct mytype; in advance means you can use that name mytype only if it's in a context where knowledge of the structure details don't have to be known. In practice, it means you can use the struct mytype * as a pointer - because this is a fixed size - but this is not allowed:

    struct ast_node; // forward declaration
    
    struct some_other_type {
        struct ast_node *nodeptr;  // this is ok
        struct ast_node  thisnode; // FAIL
        ...
    };
    

    The reason is: a pointer is a fixed size no matter what kind of data it points to so the compiler carries you a bit, but the thisnode would require knowledge of all the details. Sorry, no way to do that.

    Re: the union, you can't define any union or struct with two members with the same name because how could you reference one of them and the compiler would know which?

    Personally I'm not a fan of using typedef for structure types, so I'd do this as:

    struct ast_node; // forward struct defn
    
    struct asg_assignment {
        char *sym;
        struct ast_node *value;
    };
    
    struct ast_conjunction {
        char  * sym;
        struct ast_node* value;
    };
    
    struct ast_node {
        int node_type;
        union {
            struct ast_assignment  data_asg;
            struct ast_conjunction data_conj;
        };
    };
    

    ... but I'm not sure I can justify this by anything other than personal preference.