Я использую физическую библиотеку под названием Bullet, и у меня возникают проблемы с предотвращением включения заголовков Bullet в мой заголовок Physics.
//Physics.h
#include <BulletPhysics/btBulletDynamicsCommon.h> // Contains lots of other headers
struct Physics
{
static btDiscreteDynamicsWorld* pDynamicsWorld;
static btAlignedObjectArray<btCollisionShape*> collisionShapes;
static vec3 findSomePoint();
};
Теперь в различных других частях моего кода я могу захотеть получить доступ к структуре Physics, и для этого мне нужно включить заголовок Physics, но он также будет включать все заголовки библиотеки в любом другом файле CPP.
Я пытаюсь найти способ, чтобы заголовки библиотек были включены только в Physics.cpp, но у меня возникают проблемы с тем, чтобы компилятор распознавал типы библиотек в определении структуры, если я удаляю заголовок из Physics.h.
С некоторыми членами я могу просто объявить вперед, и это прекрасно работает, но это не сработает для не указателей или ссылочных типов, так как необходимо полное определение.
Я заметил, что если я использую пространство имен, я могу избежать объявления члена как extern, и полное определение типа не требуется. Но это приводит к странным вещам с предварительными декларациями:
struct btDiscreteDynamicsWorld; // This seems fine
template <typename T>
struct btAlignedObjectArray; // This works but seems really hacky and I'm not sure if it's even valid
namespace Physics
{
extern btDiscreteDynamicsWorld* pDynamicsWorld;
extern btAlignedObjectArray<btCollisionShape*> collisionShapes; // Complete type not needed
vec3 findSomePoint();
}
Также, используя пространство имен, я теряю возможность использовать спецификаторы доступа.
Я также подумал, что вместо того, чтобы сами члены были в структуре, я мог бы использовать функции getter и возвращать ссылки, но для возвращаемых типов также необходимо по крайней мере предварительное объявление (я думаю, что это не полный тип), и мне все равно пришлось бы объявлять этот шаблон вперед введите способ, которым я сделал ранее.
Какой лучший способ сделать это? Или я уделяю слишком много внимания предотвращению копирования лишних заголовков. Этот заголовок содержит много других заголовков, так что это довольно много, так что я не уверен, стоит ли мне волноваться или нет.
Эти две конструкции имеют одинаковое значение, за исключением того, что вторая позволяет вам выбрать, где память создается и инициализируется. Лично я бы не слишком беспокоился о рекурсивных включениях читателя, потому что ваш проект довольно большой. По сути, вы здесь используете глобальные переменные для довольно значимого общего состояния, которое обычно указывает на поток в вашем проекте. Если ваша программа многопоточная и нет пути к общему состоянию,
Я предлагаю использовать третий подход и выполнить некоторое взаимное исключение в методах получения и установки (блокировка чтения-официанта кажется подходящей), в этом случае вам необходимо каким-то образом убедиться, что состояние изменяется только через средство установки (возможно, путем возврата константной ссылки из добытчик.
Если вы собираетесь придерживаться extern
Решением и инициализацией этих объектов нетривиальным (что я предполагаю, что это не так), вы также хотите определить какую-то функцию инициализатора и убедиться, что она вызывается раньше всего, в этом случае вам, вероятно, следует рассмотреть возможность написания класса Singleton вместо что я и сделал бы.
Других решений пока нет …