-1
#ifndef STAGE_TABLE_DEFINITION_HEADER
#define STAGE_TABLE_DEFINITION_HEADER

typedef stage_table_context_t* (*stage_table_function_t)(stage_table_context_t*);

typedef struct {
    const char* stage_name;
    stage_table_function_t* function;
} stage_t;

typedef struct {
    uint32_t error_number;
    stage_t* current_stage;
} stage_table_context_t;

#endif 

Getting an unknown type error on stage_table_context_t.

The function pointer stage_table_function_t refers to stage_table_context_t and stage_table_context_t refers to stage_table_function_t.

Obviously positioning doesn't matter here since either orientation will result in an issue. Seems I need to forward declare the stage table context structure, but not sure how to do this with a typedef.

Apologies for the stupid question, I've been away from C for 6 months and I'm having a bit of a brain fart.

edit: Fixed some typo's in the code.

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
Sam D20
  • 2,535
  • 5
  • 25
  • 43
  • Does `typedef struct stage_table_context_t* (*stage_table_function_t)(struct stage_table_context_t*);` work? – Retired Ninja Jun 22 '19 at 20:39
  • I am not getting any complaints from the compiler on that line other than the unknown type error. – Sam D20 Jun 22 '19 at 20:41
  • Yes, a forward declaration is needed. There are several ways to provide one. – John Bollinger Jun 22 '19 at 20:54
  • You can't do it using two untagged (or tagless) structures. At least one of them must have a tag; for symmetry, both should have a tag, therefore. Then you can forward declare one or both structure types, and then define the structure types. Again, symmetry suggests forward declaring both. – Jonathan Leffler Jun 23 '19 at 07:00

2 Answers2

4

You just have to tell the compiler that stage_table_context_t shall be a struct; by that, you implicitly forward-declare the struct stage_table_context_t, which's actual definition may then come later. Note that a typedef does not define a struct, it just introduces an alias. So the actual definition is struct stage_table_context_t { ..., regardless of whether you introduce an alias for it or not (and regardless of which name you use for the alias).

typedef struct stage_table_context_t* (*stage_table_function_t)(struct stage_table_context_t*);

typedef struct {
    const char* stage_name;
    stage_table_function_t* function;
} stage_t;

struct stage_table_context_t {
    uint32_t error_number;
    stage_t* current_stage;
};

// optional (if you want to use "stage_table_context_t" as an alias for "struct stage_table_context_t"):
typedef struct stage_table_context_t stage_table_context_t;
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • It's confusing to describe that as telling the compiler that the type will be a struct. What's really happening would be clearer if you used a struct tag that differed from the typedef identifier: implicitly forward-declaring the struct type (which requires it to be tagged). The coincidence between tag and typedef name is incidental. – John Bollinger Jun 22 '19 at 20:51
  • Thanks this worked! What would the solution look like if I chose to emit the struct name during the structure typedef? (i.e. `typdef struct {`) – Sam D20 Jun 22 '19 at 20:54
  • 1
    @SamD20, you cannot forward declare an untagged structure type. – John Bollinger Jun 22 '19 at 20:56
  • @John Bollinger: thanks; edited the answer; hope it gets clearer now. – Stephan Lechner Jun 22 '19 at 21:07
  • @SamD20 you can combine thins in one typedef, including fully defining a structure with name and even defining several type names in one go, i.e. `typedef struct mystructtag { member definitions } mystructtypedef, anothertypedefforthesamestructure, *typedefpointertothesamestructure;` – Antti Haapala -- Слава Україні Jun 23 '19 at 03:19
  • …but see [Is it a good idea to `typedef` pointers?](https://stackoverflow.com/questions/750178/is-it-a-good-idea-to-typedef-pointers) to which the short answer is "Usually no". – Jonathan Leffler Jun 23 '19 at 07:05
4

You can make the declaration of a struct before its definition:

/* declaration */
struct foo;

.....

/* definition */
struct foo
{
   ....
};

Anywhere you write struct foo is a declaration of the struct, so you don't have to put it in a separate line, you can have it in a typedef, pointer declaration, etc.. Just be aware that sometimes, like in variable declarations of type struct foo you also need the definition (to calculate the variable size);

/* declare struct foo ..*/   
struct foo;

/* .. or declare struct foo ..*/   
typedef struct foo foo;

/* .. or declare struct foo ..*/   
struct foo *p;   

/* .. or declare struct foo */   
int bar (struct foo *p);  

/* Not valid since we don't have definition yet */
struct foo f1;   

/* definition */
struct foo
{
   ....
};

/* Valid */
struct foo f2;   

In your case you haven't given the struct a name; you've just made a typedef that is an alias for an anonymous struct. So to forward declare your struct you have to give it a name:

/* 
  forward declare the `struct stage_table_context_t` and give it a typedef alias
  with the same name as the structs name
*/ 
typedef struct stage_table_context_t stage_table_context_t;

typedef stage_table_context_t* (*stage_table_function_t)(stage_table_context_t*);

typedef struct {
    const char* stage_name;
    stage_table_function_t* function;
} stage_t;

struct stage_table_context_t{
    uint32_t error_number;
    stage_t* current_stage;
} stage_table_context_t;
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
HAL9000
  • 2,138
  • 1
  • 9
  • 20