Пытаюсь написать программу из нескольких файлов. Попытаюсь примерно изобразить, чего хочу.
Файл a.h
#ifndef _A_H
#define _A_H
#include "b.h"
typedef struct StructA {
int a;
int b;
int c;
} A;
void A_f1(A* a, B* b);
void A_f2(A* a, B* b);
void A_f3(A* a, B* b);
#endif
Файл b.h
#ifndef _B_H
#define _B_H
#include "a.h"
typedef struct StructB {
int a;
int b;
int c;
} B;
void B_f1(A* a, B* b);
void B_f2(A* a, B* b);
void B_f3(A* a, B* b);
#endif
Файл c.h
#ifndef _C_H
#define _C_H
#include "a.h"
#include "b.h"
void C_f1(A* a, B* b);
void C_f2(A* a, B* b);
void C_f3(A* a, B* b);
#endif
Файл main.c
#include "a.h"
#include "b.h"
#include "c.h"
int main() {
//some function calls...
return 0;
}
Как видно, в файле a.h нужен #include «b.h», и наоборот (иначе структуры будут не определены). Реализацию функций, прототипы которых объявлены в заголовках, опущу, но будем считать, что они дергают поля всех структур (то есть описать структуру в файлах *.c и оставив в *.h только forward declaration не получится). Собственно, код, похожий на написанный выше не компилируется, поскольку:
- В main.c включается a.h
- В a.h включается b.h
- Компилятор ругается, что в b.h используется необъявленный тип A (a.h, заинклюженый из b.h оказывается пустым, поскольку A_H уже задефайнена)
Погуглив на эту тему, наткнулся на совет: не использовать #инклюды внутри заголовочных файлов. Вместо этого вставлять forward declaration для нужных структур. Попробовал и так. Результат ниже:
Файл a.h
#ifndef _A_H
#define _A_H
//#include "b.h"
typedef struct StructA {
int a;
int b;
int c;
} A;
struct StructB;
typedef StructB B;
void A_f1(A* a, B* b);
void A_f2(A* a, B* b);
void A_f3(A* a, B* b);
#endif
Файл b.h
#ifndef _B_H
#define _B_H
//#include "a.h"
typedef struct StructB {
int a;
int b;
int c;
} B;
struct StructA;
typedef StructA A;
void B_f1(A* a, B* b);
void B_f2(A* a, B* b);
void B_f3(A* a, B* b);
#endif
Файл c.h
#ifndef _C_H
#define _C_H
//#include "a.h"
//#include "b.h"
struct StructB;
typedef StructB B;
struct StructA;
typedef StructA A;
void C_f1(A* a, B* b);
void C_f2(A* a, B* b);
void C_f3(A* a, B* b);
#endif
Файл main.c
#include "a.h"
#include "b.h"
#include "c.h"
int main() {
//some function calls...
return 0;
}
Подобный код у меня скомпилировался. Как оказалось, только на GCC 4.7. На другой машине с GCC 4.4, компилятор выдывал ошибки про повторные typedef'ы (действительно, typedef'ы в a.h и c.h, включенных в main.c повторяются). Покумекав, придумал свой вариант, который скомпилировался. Каждый из заголовков разбил на два: в первом - описание структуры и прототипы функций, а во втором - только forward definition структуры и typedef. Второй #includ'ится в другие заголовочные файлы, а первый - во все остальные. Результат, опять таки, ниже:
Файл a.h
#ifndef _A_H
#define _A_H
#include "a_short.h"
#include "b_short.h"
struct StructA {
int a;
int b;
int c;
};
void A_f1(A* a, B* b);
void A_f2(A* a, B* b);
void A_f3(A* a, B* b);
#endif
Файл a_short.h
#ifndef _A_SHORT_H
#define _A_SHORT_H
struct StructA;
typedef StructA A;
#endif
Файл b.h
#ifndef _B_H
#define _B_H
#include "a_short.h"
#include "b_short.h"
struct StructB {
int a;
int b;
int c;
};
void B_f1(A* a, B* b);
void B_f2(A* a, B* b);
void B_f3(A* a, B* b);
#endif
Файл b_short.h
#ifndef _B_SHORT_H
#define _B_SHORT_H
struct StructB;
typedef StructB B;
#endif
Файл c.h
#ifndef _C_H
#define _C_H
#include "a_short.h"
#include "b_short.h"
void C_f1(A* a, B* b);
void C_f2(A* a, B* b);
void C_f3(A* a, B* b);
#endif
Файл main.c
#include "a.h"
#include "b.h"
#include "c.h"
int main() {
//some function calls...
return 0;
}
Собственно, здесь typedef'ы защищены #ifndef'ами и поэтому появляются в коде только один раз и ошибок не возникает.
Хотелось бы узнать, как обычно решаются подобные проблемы. Варианты, которые удалось придумать мне:
- То, что получилось у меня с третьего раза. (Количество заголовочных файлов возрастает)
- Не использовать typedef'ы, использовать forward declaration (второй вариант). (В принципе, typedef'ы используются чтобы укоротить названия типа, можно обойтись и без этого)
- Забить на старый компилятор (плохая идея, да и не знаю, как в стандарте C обстоят дела с множественными typedef'ами)
- Слить всё в один заголовочный файл (в этом примере может и прокатит, но в более сложных случаях будет выглядеть страшно)
- ???
Спасибо всем, кто дочитал до конца, будет здорово, если кто-нибудь что-нибудь посоветует.