#c
Вопрос:
У меня есть 3 класса (Объект, Компонент, Преобразование)и одно пространство имен(Движок). Пространство имен движка:
#pragma once
namespace Engine {
#include <GLM/glm.hpp>
#include "../Objects/Object.h"
#include "../Objects/Components/Component.h"
#include "../Objects/Components/Transform.h"
}
Класс объектов:
#pragma once
#include <iostream>
#include <GLM/gtc/matrix_transform.hpp>
#include "../Rendering/Loader.h"
#include "../Namespaces/Engine.h"
class Object {
public:
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
Object();
Object(glm::vec3 position, glm::vec3 rotation, glm::vec3 scale);
};
Класс компонентов:
#pragma once
class Component {
public:
unsigned int id;
};
Класс преобразования:
#pragma once
#include "../../Namespaces/Engine.h"
using namespace Engine;
class Transform : public Component {
public:
Object object; <-- unknown override specifier
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
};
Я знаю, что проблема в том, что двигатель.h включен в Object.h, а Object.h включен в движок.h чтобы объектная переменная в преобразовании не знала о ее существовании, но почему и как это исправить, не удаляя объект include из движка.h так как мне нужно сделать объект и другие компоненты легко доступными, просто включив движок.h. Я знаю, что это ошибка компилятора, но есть ли какой-то способ исправить ее, не включая объект.h отдельно в другие файлы ?
Комментарии:
1. Извините, я не понимаю, почему вам нужно включить
Engine.h
это передObject
началом занятий.2. Включить цикл: преобразование.h <-> двигатель.h
3. Задавая вопросы об ошибках сборки, всегда включайте полное и полное копирование-вставку (в виде текста) ошибок, которые вы получаете.
4. Я думаю, ответ зависит от того, чего на самом деле хочет достичь ваш проект. Существует много способов разорвать цикл на графике; какой из них лучше, можете знать только вы.
5. Это первое
namespace
с#include
директивами внутри выглядит очень подозрительно и совершенно неправильно.
Ответ №1:
TL;DR избегайте циркулярных #include
директив, как чумы.
Помните, что препроцессор просто выполняет замену текста. Когда он сталкивается с #include
директивой, он буквально заменяет эту директиву (предварительно обработанным) текстом включенного файла.
В этом случае фактический результат будет варьироваться в зависимости от того, какой именно заголовок вы включаете. Ваша ошибка будет возникать каждый раз, когда вы попытаетесь напрямую включить Object.h.
Имея это в виду, давайте возьмем на себя роль препроцессора, чтобы понять, в чем проблема. Давайте начнем предварительную обработку объекта.h:
#pragma once
Мы столкнулись с нашей первой директивой. Это говорит нам о том, чтобы отметить, что мы уже включили этот файл в этот блок компиляции, и пропустить его, если нам скажут включить его снова. Мы не будем генерировать никаких выходных данных для этой директивы.
#include <iostream>
#include <GLM/gtc/matrix_transform.hpp>
#include "../Rendering/Loader.h"
Наши следующие указания. Нас попросили включить содержимое в несколько других заголовков. Я собираюсь пропустить их в этом ответе, так как они не имеют отношения к проблеме.
// contents of iostream
// contents of GLM/gtc/matrix_transform.hpp
// contents of Loader.h
#include "../Namespaces/Engine.h"
Нас попросили включить содержимое Engine.h. Для этого нам сначала нужно будет предварительно обработать его.
#pragma once
namespace Engine {
#include <GLM/glm.hpp>
Same as before, nothing too interesting here. Do note that including GLM/glm.hpp into the Engine
namespace will likely cause compiler/linker errors later in the toolchain. We’re just a preprocessor though, so we don’t care about any of that.
namespace Engine {
// contents of GLM/glm.hpp
#include "../Objects/Object.h"
Another include directive. We were told using #pragma once
to skip over future includes of Object.h though, so we won’t produce any output for this directive.
namespace Engine {
// contents of GLM/glm.hpp
#include "../Objects/Components/Component.h"
This one’s pretty simple, we’ll just preprocess and include Component.h.
namespace Engine {
// contents of GLM/glm.hpp
class Component {
public:
unsigned int id;
};
#include "../Objects/Components/Transform.h"
Другие включают директиву. Давайте начнем предварительную обработку преобразования.h
#pragma once
#include "../../Namespaces/Engine.h"
Нас попросили включить Engine.h, но a уже посоветовал нам #pragma once
пропустить будущие включения этого файла, поэтому мы не будем выдавать никаких выходных данных для этого. Это означает, что все предварительно обработанное преобразование.h будет выглядеть следующим образом:
using namespace Engine;
class Transform : public Component {
public:
Object object;
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
};
Теперь, когда мы закончили с Transform.h, мы можем вернуться на уровень выше и вставить это в Engine.h
namespace Engine {
// contents of GLM/glm.hpp
class Component {
public:
unsigned int id;
};
using namespace Engine;
class Transform : public Component {
public:
Object object;
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
};
}
И это конец Engine.h, поэтому мы можем вернуться на уровень выше и вставить это в Object.h. В Object.h нет никаких дальнейших директив препроцессора, поэтому результат препроцессора для этого файла будет выглядеть следующим образом
// contents of iostream
// contents of GLM/gtc/matrix_transform.hpp
// contents of Loader.h
namespace Engine {
// contents of GLM/glm.hpp
class Component {
public:
unsigned int id;
};
using namespace Engine;
class Transform : public Component {
public:
Object object;
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
};
}
class Object {
public:
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
Object();
Object(glm::vec3 position, glm::vec3 rotation, glm::vec3 scale);
};
Теперь вы, возможно, сможете определить проблему. На Object
класс ссылаются Transform
до того, как он определен. Обратите внимание, что он также не определен в Engine
пространстве имен, как вы, вероятно, ожидаете.
Способ решить эту проблему-убедиться, что на вашем графике включения нет циклов. Если вам нужен большой мета-заголовок, который пользователи могут включить, чтобы получить всю функциональность вашей библиотеки, это нормально, но ничто в вашей библиотеке не должно включать этот заголовок. Вы также не должны заключать свои заголовки в пространство имен извне. Это никогда не сработает. Также обратите внимание, что у этого #pragma once
есть проблемы, и стандартные #ifndef
/ #define
включенные охранники, как правило, более надежны. #pragma once
это нормально, но ИМО лучше всего этого избегать. Вы также должны отсортировать пути включения вашего проекта, чтобы вам не нужно было использовать относительные пути.
Все это говорит о том, что нечто большее, чем это, сделает свое дело. Это позволит потребителям этой библиотеки просто #include <Engine.h>
получить все или включить отдельные заголовки, чтобы получить только те части, которые они хотят:
Объект.h
#ifndef MYLIB_OBJECT_H
#define MYLIB_OBJECT_H
#include "MyLib/Rendering/Loader.h"
#include <GLM/glm.hpp>
#include <GLM/gtc/matrix_transform.hpp>
#include <iostream>
namespace Engine {
class Object {
public:
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
Object();
Object(glm::vec3 position, glm::vec3 rotation, glm::vec3 scale);
};
}
#endif
Компонент.h
#ifndef MYLIB_COMPONENT_H
#define MYLIB_COMPONENT_H
namespace Engine {
class Component {
public:
unsigned int id;
};
}
#endif
Трансформация.h
#ifndef MYLIB_TRANSFORM_H
#define MYLIB_TRANSFORM_H
#include "MyLib/Object/Object.h"
#include "MyLib/Object/Components/Component.h"
#include <GLM/glm.hpp>
namespace Engine {
class Transform : public Component {
public:
Object object;
glm::vec3 position;
glm::vec3 rotation;
glm::vec3 scale;
};
}
#endif
Двигатель.ч
// Note, #include <GLM/glm.hpp> has been moved to individual headers
#include "MyLib/Objects/Object.h"
#include "MyLib/Objects/Components/Component.h"
#include "MyLib/Objects/Components/Transform.h"
Ответ №2:
Вы пытаетесь включить пространство имен using в заголовок вашего движка, поэтому ключевое слово using, передаваемое несколько раз, в заголовке вашего трансформатора не распознает объект Engine::. избавьтесь от строки using namespace, и она будет работать
Комментарии:
1. Я попытался удалить строку «использование движка пространства имен» в Transform.h, и она выдает ту же ошибку, я даже обнаружил, что объект класса по какой-то причине отсутствовал в пространстве имен движка, и когда я немного изменил класс, он был частью пространства имен, но теперь у меня есть дополнительная ошибка в Transform.h, в которой говорится, что объект не является частью пространства имен движка, даже если он является
2. в нем говорится, что объект не является частью пространства имен движка
Ответ №3:
Я не очень разбираюсь в C , но мне кажется, что следует использовать #define
, #ifdef
, #ifndef
.
Пример:
A. h
#ifndef A_h
#define A_h
#include "B.h"
class A
{
public:
void func(B *param);
};
#endif // A_h
B. h
#ifndef B_h
#define B_h
#include "A.h"
class B
{
public:
void func(A *param);
};
#endif // B_h
Комментарии:
1. Нет, дело не в этом. OP достигает точно такого же эффекта при использовании
#pragma once
. Хотя это не является частью стандартного C , все современные компиляторы поддерживают#pragma once
его .2. Это все еще имеет циклическую зависимость. Вместо
#include "B.h"
этого вы должны просто переслать объявлениеclass B;
и сделать то же самое дляA
.3. Я использую Visual studio 2019, и там #pragma один раз делает то же самое, но это работает только в некоторых IDE, например, это не работает в коде::Блоки, я думаю
4. @ChrisHusky CB по умолчанию использует GCC в качестве компилятора и
#pragma once
работает с GCC.