2

I have the following two header files:

player.h

#ifndef _PLAYER_H_
#define _PLAYER_H_
#include "map.h"

typedef struct{
    char* name;
    int x;
    int y;
    int keyMapping;
    char symbol;
}player;
void move(map map, char* move, player player);
void setKeyMapping(int keyMap, player player);
void initPlr(char* name, int x, int y, player player);
#endif

and map.h

#ifndef _MAP_H_
#define _MAP_H_
#include "player.h"

typedef struct{
    char** levelData;
    int width;
    int height;
}map;
void init(map* map,int height, int width);
void print_map(map map, player player);
void resume(const char *resFrom, map map);
void save(const char *saveTo, map map);

#endif

When I run the make file, I get the following error:

player.h:10:11: error: unknown type name ‘map’
 void move(map map, int move, player player);

I am relatively new programmer (in C that is) and I came here is hopes that you guys could help me figure out why the header files aren't seeing each other even though I included them. The compiler I am using is gcc, and I checked to make sure that both of the header files are included in their partner C files.

I also checked out these three posts:

Include a header in another header file

Include headers in header file?

Should I use #include in headers?

EDIT:

If I edit the map.h file as I have above (I added a player parameter to print_map and an include statement) the compiler throws this error:

player.h:12:11: error: unknown type name ‘map’
 void move(map map, char* move, player player);
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Steampunkery
  • 3,839
  • 2
  • 19
  • 28
  • 4
    Please don't name your variables the same as their type. – Weather Vane Apr 13 '16 at 19:39
  • 1
    Sorry, personal habit.....I'm trying to kill it, but with programmer-created types, its really hard not to. – Steampunkery Apr 13 '16 at 19:46
  • 2
    Actually it is really easy not to, by using a naming convention such as `map_t` and `player_t`. – Weather Vane Apr 13 '16 at 19:48
  • what does the extra "_t" mean? – Steampunkery Apr 13 '16 at 19:48
  • 2
    It would tell me `map_t` is a type and distinguish that from the instances of it such as `map`. For example, suppose you have `map *map` and then you want `sizeof(map)`, what do you expect to happen? – Weather Vane Apr 13 '16 at 19:49
  • Note that names beginning with an underscore and either another underscore or a capital letter are reserved for 'the implementation' (the compiler and libraries). You should not use such names in your own code. If you copied it from the style used in system headers, then note that the system headers are part of the implementation and are obliged to use such names so that they don't interfere with you. Similarly, you should not risk interfering with them. (It's only marginally over-simplifying to say that all names beginning with an underscore are best treated as reserved.) – Jonathan Leffler Apr 14 '16 at 00:17
  • The extension `_t` is widely used to indicate 'type'; think `size_t` and `wchar_t` and `uint8_t` and `pid_t` and so on. However, be aware that POSIX reserves the extension `_t` for use in type names — so if you use it, you run the risk of porting to a new system and your type name might conflict with the system's type name, and it is a pain when that happens. If you use a systematic prefix, you may be OK; otherwise, you're on thin ice. It often isn't a problem, but you can run into it. I've seen it happen 3 or 4 times for real; a new version of the o/s conflicts with old working code. – Jonathan Leffler Apr 14 '16 at 00:56

2 Answers2

3

#includeing A.h in B.h and B.h in A.h (circular #includes) is always a problem. If you need something from B.h in A.h and something from A.h in B.h, you'll have to find a way get by with forward declarations.

In your case, you are not using anything from player.h in map.h. Simply remove the following line from map.h.

#include "player.h"
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Thank you! I don't completely understand why this works, shouldn't it just ignore it if it isn't used? – Steampunkery Apr 13 '16 at 19:38
  • 1
    @Steampunkery, processing of `#include` statements and compiling the code are done in two different steps that are completely independent. The answer to your question is: "No, the processor cannot ignore an `#include` statement." – R Sahu Apr 13 '16 at 19:41
  • see edit, I just had a need to use player.h in map.h – Steampunkery Apr 13 '16 at 23:02
  • You can't use `map` in player.h and `player` in map.h. You'll have to use a pointer in one them -- either use `map*` in player.h or `player*` in map.h. – R Sahu Apr 14 '16 at 03:30
0

If you can't avoid the mutual references to the types in the functions for the two types, consider whether they really belong in two headers. Maybe you should put them into one header? After all, the compiler is always going to end up including both anywy.

Alternatively, consider using opaque types:

When using opaque types, you use pointers to those types in the interfaces to functions. Adapting your code:

map.h

#ifndef MAP_H_INCLUDED
#define MAP_H_INCLUDED

struct Map;
struct Player;

#include "player.h"

typedef struct Map {
    char** levelData;
    int width;
    int height;
} Map;

void init(struct Map* map,int height, int width);
void print_map(struct Map *map, struct Player *player);
void resume(const char *resFrom, struct Map *map);
void save(const char *saveTo, struct Map *map);

#endif

player.h

#ifndef PLAYER_H_INCLUDED
#define PLAYER_H_INCLUDED

struct Map;
struct Player;

#include "map.h"

typedef struct Player{
    char* name;
    int x;
    int y;
    int keyMapping;
    char symbol;
} Player;

void move(struct Map map, char* move, struct Player *player);
void setKeyMapping(int keyMap, struct Player *player);
void initPlr(char* name, int x, int y, struct Player *player);

#endif

The arguments are now pointers to maps or pointers to players, not direct copies of the structures.

Either file (or both files) can be specified in a source file using these, and the code will compile with both. Note, too, that I've separated the typedef names from the variable names. Using the same name for both is risky. Using the same name for the tag and the typedef is not a problem; the typedef names are in the ordinary namespace but the tag names are in the separate tag namespace. (C++ achieves essentially the same effect without an explicit typedef operation: after declaring struct SomeTag, you can use SomeTag to refer to the type.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278