реклама на сайте
подробности

 
 
> Разбивка большого заголовочного файла на несколько, покритикуйте решение
Сергей Борщ
сообщение Feb 19 2015, 09:45
Сообщение #1


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Задача: есть некий большой заголовочный файл. Ну например:
Код
#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 и вместо сообщения об ошибке все будет корректно собираться, хотя тут есть рекурсия включений.

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


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов (1 - 9)
CrimsonPig
сообщение Feb 19 2015, 11:20
Сообщение #2


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(Сергей Борщ @ Feb 19 2015, 09:45) *
Задача: есть некий большой заголовочный файл. Ну например:
Сами классы довольно большие (в экран не влезают), плюс еще inline-функции, в общем файл получился большой. Ненавижу листать большие файлы, поэтому появилось желание разбить его на несколько:



Работает и ладно...
- можно вынести имплементрацию inline - функций в отдельный файл, типа *.inl и включать его в хедер где-нибудь под конец.
- раскидать хедеры для только внутреннего потребления и для внешнего по разным каталогам, шоб было больше проблем тому, кто хочет зачем-то включать приватные заголовочные файлы.
- открыть для себя редакторы с code folding sm.gif
- проблемы проектирования не решаются техническими методами. Если кто-то разродился классом, определение которого занимает 20 экранов, то тут не кровати переставлять надо, а пересматривать концепцию.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Feb 19 2015, 12:23
Сообщение #3


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(CrimsonPig @ Feb 19 2015, 13:20) *
- можно вынести имплементрацию inline - функций в отдельный файл, типа *.inl и включать его в хедер где-нибудь под конец.
Начинаются проблемы с подсветкой синтаксиса.

Цитата(CrimsonPig @ Feb 19 2015, 13:20) *
- открыть для себя редакторы с code folding sm.gif
Не спасает.
Цитата(CrimsonPig @ Feb 19 2015, 13:20) *
Если кто-то разродился классом, определение которого занимает 20 экранов, то тут не кровати переставлять надо, а пересматривать концепцию.
Пересмотрите концепцию стандартной библиотеки С++ sm.gif


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Feb 19 2015, 12:54
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(Сергей Борщ @ Feb 19 2015, 12:23) *
Пересмотрите концепцию стандартной библиотеки С++ sm.gif


Благородный дон в одиночку переделывает stl или boost ? Сурово.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Feb 20 2015, 13:26
Сообщение #5


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(CrimsonPig @ Feb 19 2015, 14:54) *
Благородный дон в одиночку переделывает stl или boost ?
Нет, всего лишь пишу USB-стек для STM32. stl привел как пример классов "на 20 экранов", концепцию которых пересматривать никто не собирается.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Feb 20 2015, 14:44
Сообщение #6


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(Сергей Борщ @ Feb 20 2015, 13:26) *
Нет, всего лишь пишу USB-стек для STM32. stl привел как пример классов "на 20 экранов", концепцию которых пересматривать никто не собирается.


А при чем здесь концепция stl ? Если вынести имплементацию методов шаблонов в отдельный файл, то само определение класса с объявлением методов будет выглядеть вполне пристойно.
Никто не заставляет писать шаблонные классы + имплементацию методов в одном месте.
Я имел в виду, что если у класса 120 членов-данных разного типа + набор из 300 разномастных методов, которые непойми что делают, то тут не кровати переставлять надо.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Feb 20 2015, 15:42
Сообщение #7


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(CrimsonPig @ Feb 20 2015, 16:44) *
Если вынести имплементацию методов шаблонов в отдельный файл, то само определение класса с объявлением методов будет выглядеть вполне пристойно.
Если вынести в файл исходника, методы не будут встраиваемыми. Если вынести в заголовочный - то имеем проблему, описанную в первом сообщении.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Feb 20 2015, 15:56
Сообщение #8


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(Сергей Борщ @ Feb 20 2015, 15:42) *
Если вынести в файл исходника, методы не будут встраиваемыми. Если вынести в заголовочный - то имеем проблему, описанную в первом сообщении.


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

В незамутненной теории надо публиковать чистые интерфейсы, а всю имплементацию прятать далеко-далеко. Но это надо много обдумывать и переделывать. Затраченные на это усилия не всегда оправдываются. То ли у Саттера, то ли у кого еще из современных столпов была книжка с главой "separating interface and implementation", там все это обсасывалось (включая паттерны типа pImpl).
Go to the top of the page
 
+Quote Post
mdmitry
сообщение Feb 20 2015, 18:13
Сообщение #9


Начинающий профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 25-10-06
Из: СПб
Пользователь №: 21 648



Цитата(Сергей Борщ @ Feb 19 2015, 13:45) *
...
Скорее всего я изобрел велосипед, но в header_b.h сделал так:
...
Теперь я могу включать в любое нужное место как header_a.h, так и header_b.h и вместо сообщения об ошибке все будет корректно собираться, хотя тут есть рекурсия включений.

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


Сергей, для меня предпочтителен вариант :
Код
[/code]
#ifndef HEADER_B_H__
#define HEADER_B_H__

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

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

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


--------------------
Наука изощряет ум; ученье вострит память. Козьма Прутков
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Feb 20 2015, 20:49
Сообщение #10


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



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


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 24th July 2025 - 23:56
Рейтинг@Mail.ru


Страница сгенерированна за 0.0144 секунд с 7
ELECTRONIX ©2004-2016