Сергей Борщ 132 19 февраля, 2015 Опубликовано 19 февраля, 2015 · Жалоба Задача: есть некий большой заголовочный файл. Ну например: #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 и вместо сообщения об ошибке все будет корректно собираться, хотя тут есть рекурсия включений. На мой взгляд все выглядит красиво, но возможно я не вижу каких-то подводных камней? Ваше мнение - имеет такой велосипед право на жизнь? Или может есть другие, еще более красивые решения? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
CrimsonPig 0 19 февраля, 2015 Опубликовано 19 февраля, 2015 · Жалоба Задача: есть некий большой заголовочный файл. Ну например: Сами классы довольно большие (в экран не влезают), плюс еще inline-функции, в общем файл получился большой. Ненавижу листать большие файлы, поэтому появилось желание разбить его на несколько: Работает и ладно... - можно вынести имплементрацию inline - функций в отдельный файл, типа *.inl и включать его в хедер где-нибудь под конец. - раскидать хедеры для только внутреннего потребления и для внешнего по разным каталогам, шоб было больше проблем тому, кто хочет зачем-то включать приватные заголовочные файлы. - открыть для себя редакторы с code folding :) - проблемы проектирования не решаются техническими методами. Если кто-то разродился классом, определение которого занимает 20 экранов, то тут не кровати переставлять надо, а пересматривать концепцию. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 132 19 февраля, 2015 Опубликовано 19 февраля, 2015 · Жалоба - можно вынести имплементрацию inline - функций в отдельный файл, типа *.inl и включать его в хедер где-нибудь под конец.Начинаются проблемы с подсветкой синтаксиса. - открыть для себя редакторы с code folding :)Не спасает. Если кто-то разродился классом, определение которого занимает 20 экранов, то тут не кровати переставлять надо, а пересматривать концепцию.Пересмотрите концепцию стандартной библиотеки С++ :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
CrimsonPig 0 19 февраля, 2015 Опубликовано 19 февраля, 2015 · Жалоба Пересмотрите концепцию стандартной библиотеки С++ :) Благородный дон в одиночку переделывает stl или boost ? Сурово. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 132 20 февраля, 2015 Опубликовано 20 февраля, 2015 · Жалоба Благородный дон в одиночку переделывает stl или boost ?Нет, всего лишь пишу USB-стек для STM32. stl привел как пример классов "на 20 экранов", концепцию которых пересматривать никто не собирается. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
CrimsonPig 0 20 февраля, 2015 Опубликовано 20 февраля, 2015 · Жалоба Нет, всего лишь пишу USB-стек для STM32. stl привел как пример классов "на 20 экранов", концепцию которых пересматривать никто не собирается. А при чем здесь концепция stl ? Если вынести имплементацию методов шаблонов в отдельный файл, то само определение класса с объявлением методов будет выглядеть вполне пристойно. Никто не заставляет писать шаблонные классы + имплементацию методов в одном месте. Я имел в виду, что если у класса 120 членов-данных разного типа + набор из 300 разномастных методов, которые непойми что делают, то тут не кровати переставлять надо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 132 20 февраля, 2015 Опубликовано 20 февраля, 2015 · Жалоба Если вынести имплементацию методов шаблонов в отдельный файл, то само определение класса с объявлением методов будет выглядеть вполне пристойно.Если вынести в файл исходника, методы не будут встраиваемыми. Если вынести в заголовочный - то имеем проблему, описанную в первом сообщении. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
CrimsonPig 0 20 февраля, 2015 Опубликовано 20 февраля, 2015 · Жалоба Если вынести в файл исходника, методы не будут встраиваемыми. Если вынести в заголовочный - то имеем проблему, описанную в первом сообщении. Ну нак я же советовал, положите заголовочный файл с имплементацией инлайновых методов во внутренний каталог, подальше от публичных хедеров библиотеки. Если глупый юзер захочет включить в свой проект левый хедер для внутреннего использования - так это его проблемы, пусть с ошибками компиляции борется сам. В незамутненной теории надо публиковать чистые интерфейсы, а всю имплементацию прятать далеко-далеко. Но это надо много обдумывать и переделывать. Затраченные на это усилия не всегда оправдываются. То ли у Саттера, то ли у кого еще из современных столпов была книжка с главой "separating interface and implementation", там все это обсасывалось (включая паттерны типа pImpl). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mdmitry 0 20 февраля, 2015 Опубликовано 20 февраля, 2015 · Жалоба ... Скорее всего я изобрел велосипед, но в 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] Перемещено, чтобы препроцессор обрабатывал гарантированно только вновь включаемые файлы. В этом случае рекурсия не убирается? Применяю именно такой подход. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 132 20 февраля, 2015 Опубликовано 20 февраля, 2015 · Жалоба Сергей, для меня предпочтителен вариант :Такой вариант работать будет только в том случае, если в исходник включается header_a.h. А если включается header_b.h, то он включит header_a.h, но внутри header_a.h, там, где ему требуется содержимое header_b.h, это содержимое включено не будет, так как #define HEADER_B_H__ уже было определено и будет куча ошибок. В моем варианте тоже нет рекурсии, каждый файл защищается своим #ifndef #define, но при этом при подключении к исходнику любого из них подключаются оба в правильном порядке. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться