I am working on a proyect that uses several libraries created by myself. Some of these libraries are dependent on one another, and in order to avoid circular dependency problems I have used the forward declaration header method, which consists of creating a separate file for the forward declarations, as explained in one of the answers of this post: Resolve build errors due to circular dependency amongst classes.
In some headers I created structs that contained pointers to structs from other headers, and there was no problem. However, now I need to declare a few structs that contain structs instead of pointers to structs. For instance, these are the declarations I make in a file called Capa2.h.
#include "Capa2_fwd.h"
#include "Net_fwd.h"
#include "Grafico.h"
#define TAM_MAX_PAYLOAD 248
#pragma pack(push, 1)
struct cab_arp_ {
short tipo_hw;
short tipo_proto;
char lon_dir_hw;
char lon_dir_proto;
short cod_op;
dir_mac_t mac_origen;
unsigned int ip_origen;
dir_mac_t mac_destino;
unsigned int ip_destino;
};
struct cab_ethernet_ {
dir_mac_t mac_destino;
dir_mac_t mac_origen;
short type;
char payload[TAM_MAX_PAYLOAD];
unsigned int FCS;
};
#pragma pack(pop)
struct entrada_arp_ {
dir_ip_t dir_ip;
dir_mac_t dir_mac;
char nombre_if[16];
};
struct tabla_arp_ {
Lista_t *entradas_arp;
};
These are the forward declarations in Capa2_fwd.h.
typedef struct cab_arp_ cab_arp_t;
typedef struct cab_ethernet_ cab_ethernet_t;
typedef struct entrada_arp_ entrada_arp_t;
typedef struct tabla_arp_ tabla_arp_t;
I get these errors.
In file included from ./inc/Net.h:10, from ./inc/Grafico.h:7, from prueba.c:2:
./inc/Capa2.h:18:12: error: field ‘mac_origen’ has incomplete type
18 | dir_mac_t mac_origen;
./inc/Capa2.h:20:12: error: field ‘mac_destino’ has incomplete type
20 | dir_mac_t mac_destino;
./inc/Capa2.h:25:12: error: field ‘mac_destino’ has incomplete type
25 | dir_mac_t mac_destino;
./inc/Capa2.h:26:12: error: field ‘mac_origen’ has incomplete type
26 | dir_mac_t mac_origen;
./inc/Capa2.h:35:11: error: field ‘dir_ip’ has incomplete type
35 | dir_ip_t dir_ip;
./inc/Capa2.h:36:12: error: field ‘dir_mac’ has incomplete type
36 | dir_mac_t dir_mac;
The types that cause the errors are declared in Net_fwd.h
typedef struct dir_ip_ dir_ip_t;
typedef struct dir_mac_ dir_mac_t;
typedef struct prop_nodo_ prop_nodo_t;
typedef struct prop_intf_ prop_intf_t;
And the structs are declared in Net.h
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "Grafico.h"
#include "Net_fwd.h"
#include "Capa2.h"
#define TAM_DIR_MAC 6
#define TAM_DIR_IP 16
#pragma pack(push, 1)
typedef struct dir_ip_ {
char dir_ip[TAM_DIR_IP];
} dir_ip_t;
typedef struct dir_mac_ {
char dir_mac[TAM_DIR_MAC];
} dir_mac_t;
#pragma pack (pop)
struct prop_nodo_ {
bool tiene_dir_loopback;
dir_ip_t dir_loopback;
};
struct prop_intf_ {
bool tiene_dir_ip;
dir_ip_t dir_ip;
dir_mac_t dir_mac;
char mascara;
};
I understand that a forward declaration is not enough if I want to put a struct inside a struct, but I don't know how to fix this.
CodePudding user response:
When you declare a variable/field that is a pointer to a struct type, the compiler doesn't need to know the definition until you dereference the pointer in the code as the size of the variable/field is 8 bytes (on a 64 bit architecture). When you declare a variable/field to be a struct type, the compiler has to know what the size of the struct is to allocate enough memory. The typedef to rename the structs can be forward as that doesn't need the size. But if you want to declare fields with structured types, then the struct declarations have to appear in topological order. So the compiler has to see Net.h before Capa2.h.
CodePudding user response:
UPDATE 2
Okay, you have only two options. Either use pointers to your structs or change the order of the includes. If you are not able to come up with a working order for your includes then this most probably means that you have a bad design. If so, reconsider your design.
Using pointers is straightforward but to sort your includes in a working order you may use an intermediate header file like below to simplify things:
Capa2_fwd.h
, Net_fwd.h
, Capa2.h
, and Net.h
can look similar to what you have posted. Then you would have:
Intermediate.h (Note that order of the includes is important)
#include "Capa2.h"
#include "Net.h"
Main.cpp
#include "Intermediate.h"
int _tmain(int argc, _TCHAR* argv[])
{
cab_arp_ x;
prop_intf_ y;
return 0;
}
UPDATE 1
So, based on your comment, the only approach is to change how you are including everything. Does the below scenario fit your needs?
First, forget about forward declarations and then:
Capa2.h
#ifndef _INC_CAPA2_H
#define _INC_CAPA2_H
#include "Net.h"
#define TAM_MAX_PAYLOAD 248
#pragma pack(push, 1)
struct cab_arp_ {
short tipo_hw;
short tipo_proto;
char lon_dir_hw;
char lon_dir_proto;
short cod_op;
dir_mac_t mac_origen;
unsigned int ip_origen;
dir_mac_t mac_destino;
unsigned int ip_destino;
};
struct cab_ethernet_ {
dir_mac_t mac_destino;
dir_mac_t mac_origen;
short type;
char payload[TAM_MAX_PAYLOAD];
unsigned int FCS;
};
#pragma pack(pop)
struct entrada_arp_ {
dir_ip_t dir_ip;
dir_mac_t dir_mac;
char nombre_if[16];
};
#endif
Net.h
#ifndef _INC_NET_H
#define _INC_NET_H
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "Capa2.h"
#define TAM_DIR_MAC 6
#define TAM_DIR_IP 16
#pragma pack(push, 1)
typedef struct dir_ip_ {
char dir_ip[TAM_DIR_IP];
} dir_ip_t;
typedef struct dir_mac_ {
char dir_mac[TAM_DIR_MAC];
} dir_mac_t;
#pragma pack (pop)
struct prop_nodo_ {
bool tiene_dir_loopback;
dir_ip_t dir_loopback;
};
struct prop_intf_ {
bool tiene_dir_ip;
dir_ip_t dir_ip;
dir_mac_t dir_mac;
char mascara;
};
#endif
Main.cpp
#include "Capa2.h"
#include "Net.h"
int _tmain(int argc, _TCHAR* argv[])
{
cab_arp_ x;
prop_intf_ y;
return 0;
}
This is compiling successfully on my end and should be fine for yours too unless your project has some other complexity that you didn't mention.
Original Answer
It is difficult to advise on this without knowing the contents of all of your headers. The first thing that comes to my mind is to use header guards.
Wrap the contents of each of your header files in a #ifndef
directive like below. Make sure to use different names for each header (_INC_CAPA2_FWD
, _INC_NET_FWD
, etc.):
#ifndef _INC_CAPA2_FWD
#define _INC_CAPA2_FWD
// header content goes here
#endif // _INC_CAPA2_FWD
This way, you might also not need to do forward declarations.