Перейти к содержанию
    

Разбивка большого заголовочного файла на несколько

Задача: есть некий большой заголовочный файл. Ну например:

#ifndef HEADER_H__
#define HEADER_H__

class a
{
...
    class b
    {
    ...
    };
};

#endif // HEADER_H__

Сами классы довольно большие (в экран не влезают), плюс еще inline-функции, в общем файл получился большой. Ненавижу листать большие файлы, поэтому появилось желание разбить его на несколько:

 

#ifndef HEADER_A_H__
#define HEADER_A_H__

class a
{
...
    class b;
};

#include "header_b.h"

.... // тут идут определения встраиваемых функций-членов класса a, которым нужно знать о внутреннем устройстве класса b

#endif // HEADER_A_H__

=================================

#ifndef HEADER_B_H__
#define HEADER_B_H__

class a::b
{
...
};
#endif // HEADER_B_H__

Но в этом случае header_b.h сам по себе интереса не представляет, его можно включать в исходник только через header_a.h

Для вменяемой диагностики неправильного включения обычно применяется такой метод:

#ifndef HEADER_B_H__
#define HEADER_B_H__

#ifndef HEADER_A_H__
  #error "Include header_a.h instead of this file"
#endif

class a::b
{
...
};
#endif // HEADER_B_H__

Вроде все работает правильно, но не нравится мне один момент: в файле исходника, в котором определены невстраиваемые функции-члены класса a::b вместо логичного #include "header_b.h" придется включать header_a.h

 

Скорее всего я изобрел велосипед, но в header_b.h сделал так:

#include "header_a.h"
#ifndef HEADER_B_H__
#define HEADER_B_H__

class a::b
{
...
};
#endif // HEADER_B_H__

Теперь я могу включать в любое нужное место как header_a.h, так и header_b.h и вместо сообщения об ошибке все будет корректно собираться, хотя тут есть рекурсия включений.

 

На мой взгляд все выглядит красиво, но возможно я не вижу каких-то подводных камней? Ваше мнение - имеет такой велосипед право на жизнь? Или может есть другие, еще более красивые решения?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Задача: есть некий большой заголовочный файл. Ну например:

Сами классы довольно большие (в экран не влезают), плюс еще inline-функции, в общем файл получился большой. Ненавижу листать большие файлы, поэтому появилось желание разбить его на несколько:

 

 

Работает и ладно...

- можно вынести имплементрацию inline - функций в отдельный файл, типа *.inl и включать его в хедер где-нибудь под конец.

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

- открыть для себя редакторы с code folding :)

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

- можно вынести имплементрацию inline - функций в отдельный файл, типа *.inl и включать его в хедер где-нибудь под конец.
Начинаются проблемы с подсветкой синтаксиса.

 

- открыть для себя редакторы с code folding :)
Не спасает.

Если кто-то разродился классом, определение которого занимает 20 экранов, то тут не кровати переставлять надо, а пересматривать концепцию.
Пересмотрите концепцию стандартной библиотеки С++ :)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Пересмотрите концепцию стандартной библиотеки С++ :)

 

Благородный дон в одиночку переделывает stl или boost ? Сурово.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Благородный дон в одиночку переделывает stl или boost ?
Нет, всего лишь пишу USB-стек для STM32. stl привел как пример классов "на 20 экранов", концепцию которых пересматривать никто не собирается.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нет, всего лишь пишу USB-стек для STM32. stl привел как пример классов "на 20 экранов", концепцию которых пересматривать никто не собирается.

 

А при чем здесь концепция stl ? Если вынести имплементацию методов шаблонов в отдельный файл, то само определение класса с объявлением методов будет выглядеть вполне пристойно.

Никто не заставляет писать шаблонные классы + имплементацию методов в одном месте.

Я имел в виду, что если у класса 120 членов-данных разного типа + набор из 300 разномастных методов, которые непойми что делают, то тут не кровати переставлять надо.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

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

 

В незамутненной теории надо публиковать чистые интерфейсы, а всю имплементацию прятать далеко-далеко. Но это надо много обдумывать и переделывать. Затраченные на это усилия не всегда оправдываются. То ли у Саттера, то ли у кого еще из современных столпов была книжка с главой "separating interface and implementation", там все это обсасывалось (включая паттерны типа pImpl).

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...

Скорее всего я изобрел велосипед, но в header_b.h сделал так:

...

Теперь я могу включать в любое нужное место как header_a.h, так и header_b.h и вместо сообщения об ошибке все будет корректно собираться, хотя тут есть рекурсия включений.

 

На мой взгляд все выглядит красиво, но возможно я не вижу каких-то подводных камней? Ваше мнение - имеет такой велосипед право на жизнь? Или может есть другие, еще более красивые решения?

 

Сергей, для меня предпочтителен вариант :



#ifndef HEADER_B_H__
#define HEADER_B_H__

#include "header_a.h" <<<<<<<<< перемещено

class a::b
{
...
};
#endif // HEADER_B_H__[/code]

Перемещено, чтобы препроцессор обрабатывал гарантированно только вновь включаемые файлы. В этом случае рекурсия не убирается? Применяю именно такой подход.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сергей, для меня предпочтителен вариант :
Такой вариант работать будет только в том случае, если в исходник включается header_a.h. А если включается header_b.h, то он включит header_a.h, но внутри header_a.h, там, где ему требуется содержимое header_b.h, это содержимое включено не будет, так как #define HEADER_B_H__ уже было определено и будет куча ошибок. В моем варианте тоже нет рекурсии, каждый файл защищается своим #ifndef #define, но при этом при подключении к исходнику любого из них подключаются оба в правильном порядке.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...