Включаемый файл включает самого себя

Есть три файла: DllMain.h, Player.h и Chest.h. У каждого из этих файлов есть так же .cpp где находится сам код. В заголовочных файлах только объявления и разные #include. Каждый из этих трёх файлов должен иметь доступ к вызовам OpenGL функций (GL.h) DllMain должен иметь возможность вызывать функции draw() из Player.h и Chest.h. А Player.h и Chest.h должны иметь возможность вызывать OpenGL функции. Если подключать GL.h ко всем трем файлам, то будет много разных ошибок. Если в Player.h и Chest.h написать #include "DllMain.h" в котором прописано #include "GL.h", то нужно ещё подключить Player.h и Chest.h в DllMain.h иначе я из DllMain.h не буду иметь возможность вызывать draw() в Player.h и Chest.h. И тут появляется ошибка когда файл включает самого себя. Как решить эту проблему более правильно?


Ответы (2 шт):

Автор решения: Rikitikitavi

Судя по описанию в cpp-файл два раза попадает один и тот же заголовочный файл, например:

 DllMain.cpp <- Chest.h <- Player.h
             <- Player.h

То еcть в логике DllMain необходимо работать с двумя сущностями: Player и Chest. В свою очередь сущности Chest нужен Player.

DllMain.cpp:

 #include "Chest.h"
 #include "Player.h"
 ...

Chest.h:

  #include "Player.h"

Chest.cpp:

  #include "Chest.h" 

Это вполне адекватная ситуация, нюанс в том что компилятор при обработке #include вставляет все содержимое файла, в итоге получаем ошибки множественного определения. Чтобы это исправить нужно в начале каждого заголовочного файла писать #pragma once, либо:

Chest.h:

 #if defined CHEST_H
 #define CHEST_H
 ...
 #endif    
→ Ссылка
Автор решения: Никита Самоуков

У С++ одна из основных проблем это хедеры.

Хедеры это такой хак, чтобы можно было писать большие программы, несмотря на то что рефлексии и рантайма нет. Всё что делает препроцессор на #include , это вставляет содержимое файла вместо этой строки, и НИЧЕГО кроме этого.

Пускай есть 4 файла

a.h

int a(int l);

a.cpp

#include "a.h" 
#include "b.h" 
int a(int l){
    if(l<=0) return 1;
    return b(l-1)+2;
}

b.h

int b(int l);

b.cpp

#include "a.h" 
#include "b.h" 
int b(int l){
    if(l<=0) return 1;
    return a(l-1)*3;
}

Фактически компилятору серавно что в хедерах, пока он не встретит инклуд, он их будет игнорировать. И для него эти файлы будут выглядеть так:

a_complie_unit

int a(int l);
int b(int l);
int a(int l){
    if(l<=0) return 1;
    return b(l-1)+2;
}

b_complie_unit

int a(int l);
int b(int l);
int b(int l){
    if(l<=0) return 1;
    return a(l-1)*3;
}

Так как объявление классов можно делать только 1 раз, и чтобы не вставлять 100 одинаковых копий текста, во всех компилчторах есть второй хак:

#pragma once

всё что он делает, это запрещает второй раз включать этот файл.

Теперь что касается 3 файлов друг на друга: Включать хедер в хедере нужно если:

  1. нужны объявления клссов/структур
  2. или нужны функции из хедеров для темплейтовых или инлайновых функций(для обычных функций вллючать хедер надо в .cpp а не в .h)

Также стоит учесть что для передачи аргументов и возврата по ссылке, а также возвращаемого значения функций достаточно форвард декларации.

И если есть 3 хедера включающих друг друга значит это реально где-то проблема, не должно быть циклического инклуда, прагма онц тут только скроет проблему, но не решит её, она вернётся потом.

Если подключать GL.h ко всем трем файлам, то будет много разных ошибок.

Счегобы? Я всегда так делал. https://gitlab.com/nikitasamoukov/irambis/-/blob/master/irambis/src/pch.h Подключил GLAD ко всем файлам .cpp через предкомпилированный заголовок и всё.

#include <glad/glad.h>
→ Ссылка