Search code examples
cheaderprojecttypedef

Use typedef from one header in another and vice versa


Suppose I have such a project structure:

main.c

#include "hashtable.h"
#include "list.h"

int main()
{
    hash_table ht = calloc(1, sizeof(htable));
    cmp_function f;
    TLDI list;
    return 0;
}

hashtable.h

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef _HASH_TABLE_
#define _HASH_TABLE_
#include "list.h"

typedef int (*hash_function)(void*, int);

typedef struct _hasht_{
  int maxElemNumber;
  hash_function hf;
  TLDI* key_array;
} htable, *hash_table;

void test2(cmp_function cmp);

#endif

list.h

#include "hashtable.h"

#ifndef _LINKED_LIST_
#define _LINKED_LIST_

typedef int (*cmp_function)(void*, void*);

typedef struct _node_ {
  void *info;
  struct _node_ *pre, *urm;
} TNode, *TLDI;

int test(hash_table ht);

#endif

and another two C files:

hash_func.c

#include "hashtable.h"

void test2(cmp_function cmp)
{
    printf("test\n");
}

list_func.c

#include "list.h"

int test(hash_table ht)
{
    return 1;
}

I want to use in hashtable.h a typedef from list.h, it's typedef struct...},*TLDI;. In the same way, list.h uses a typedef struct ...},*hash_table; from hashtable.h. Can I do something like this or I'm wrong? Cause I get this error while compiling whole project:

In file included from hashtable.h:7,
                 from main.c:1:
list.h:14:10: error: unknown type name ‘hash_table’
   14 | int test(hash_table ht);
In file included from hashtable.h:7,
from hash_func.c:1:
list.h:14:10: error: unknown type name ‘hash_table’
   14 | int test(hash_table ht);

I'm not strong in typedef and headers, but if I would get an answer to this question or at least a source from where I could find out more about them I would be very grateful.


Solution

  • Two headers that rely to each other are not a show stopper if well-formed. What I observe is that your include guards don't enclose the full header but only part of it, this I think is wrong. The right way to use include guards is shown in this

    example header some_component.h:

    #ifndef SOME_COMPONENT_H
    #define SOME_COMPONENT_H
    
    // include whatever you need here (*after* the opening guard):
    #include "some_other_component.h"
    
    // start type definitions and declarations *after* includes:
    struct some_component_t {
      // ...
    };
    
    #endif
    

    This way, you headers will work most consistently:

    • either read completely
    • or completely ignored

    I advise you to avoid placing definitions before includes, as this allows you to modify the content of the included content. What looks like a tempting idea at first, turns into a confusing nightmare in the long run in the vast majority of cases.

    Another point is that if the definitions in the two headers really rely on each other, you should rethink your design.

    Also, it's not clear why void test2(cmp_function cmp); which relies on cmp_function is declared in hashtable.h and why int test(hash_table ht); which relies on hash_table is declared in list.h; to me this seems like you were mixing up things here. In other words, by switching places of some declarations, you'd get rid of most of the entanglement.

    You should also know that typedefs and pointers are allowed on incomplete types, so it's possible to declare a pointer to a structure that is not yet defined. So, for example, the following compiles:

    typedef int (*hash_function)(void*,int);
    typedef int (*cmp_function)(void*,void*);
    
    typedef struct _hasht_ hasht, *hash_table;
    typedef struct _node_ TNode, *TLDI;
    
    struct _node_ {
      void *info;
      struct _node_ *pre, *urm;
    };
    
    struct _hasht_{
      int maxElemNumber;
      hash_function hf;
      TLDI* key_array;
    };
    

    ... as does this (version without struct typedefs):

    struct _node_ {
      void *info;
      struct _node_ *pre, *urm;
    };
    
    typedef int (*hash_function)(void*,int);
    
    struct _hasht_{
      int maxElemNumber;
      hash_function hf;
      struct _node_** key_array;
    };