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

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
 |
Ответов
(1 - 9)
|
Feb 19 2015, 11:20
|

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

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

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

|
Цитата(CrimsonPig @ Feb 19 2015, 13:20)  - можно вынести имплементрацию inline - функций в отдельный файл, типа *.inl и включать его в хедер где-нибудь под конец. Начинаются проблемы с подсветкой синтаксиса. Цитата(CrimsonPig @ Feb 19 2015, 13:20)  - открыть для себя редакторы с code folding  Не спасает. Цитата(CrimsonPig @ Feb 19 2015, 13:20)  Если кто-то разродился классом, определение которого занимает 20 экранов, то тут не кровати переставлять надо, а пересматривать концепцию. Пересмотрите концепцию стандартной библиотеки С++
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Feb 20 2015, 14:44
|

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

|
Цитата(Сергей Борщ @ Feb 20 2015, 13:26)  Нет, всего лишь пишу USB-стек для STM32. stl привел как пример классов "на 20 экранов", концепцию которых пересматривать никто не собирается. А при чем здесь концепция stl ? Если вынести имплементацию методов шаблонов в отдельный файл, то само определение класса с объявлением методов будет выглядеть вполне пристойно. Никто не заставляет писать шаблонные классы + имплементацию методов в одном месте. Я имел в виду, что если у класса 120 членов-данных разного типа + набор из 300 разномастных методов, которые непойми что делают, то тут не кровати переставлять надо.
|
|
|
|
|
Feb 20 2015, 15:56
|

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

|
Цитата(Сергей Борщ @ Feb 20 2015, 15:42)  Если вынести в файл исходника, методы не будут встраиваемыми. Если вынести в заголовочный - то имеем проблему, описанную в первом сообщении. Ну нак я же советовал, положите заголовочный файл с имплементацией инлайновых методов во внутренний каталог, подальше от публичных хедеров библиотеки. Если глупый юзер захочет включить в свой проект левый хедер для внутреннего использования - так это его проблемы, пусть с ошибками компиляции борется сам. В незамутненной теории надо публиковать чистые интерфейсы, а всю имплементацию прятать далеко-далеко. Но это надо много обдумывать и переделывать. Затраченные на это усилия не всегда оправдываются. То ли у Саттера, то ли у кого еще из современных столпов была книжка с главой "separating interface and implementation", там все это обсасывалось (включая паттерны типа pImpl).
|
|
|
|
|
Feb 20 2015, 18:13
|

Начинающий профессионал
    
Группа: Свой
Сообщений: 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__ Перемещено, чтобы препроцессор обрабатывал гарантированно только вновь включаемые файлы. В этом случае рекурсия не убирается? Применяю именно такой подход.
--------------------
Наука изощряет ум; ученье вострит память. Козьма Прутков
|
|
|
|
|
Feb 20 2015, 20:49
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|