Объясните, пожалуйста, в чем выражается эта нехватка модульной системы в C++? Все классы в отдельных единицах трансляции обычно находятся. Чего не хватает?
В книжке Герба Саттера про Pimpl idiom написано, что:
it does help compensate for C++'s lack of a module system.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Объясните, пожалуйста, в чем выражается эта нехватка модульной системы в C++?
Временем компиляции выражается. И плясками с бубнами при подключении сторонних библиотек.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Привет!
МР>Объясните, пожалуйста, в чем выражается эта нехватка модульной системы в C++? Все классы в отдельных единицах трансляции обычно находятся. Чего не хватает?
Выскажу своими словами мой взгляд на проблему:
Когда у тебя C++ ный проект разростается и перестаёт быть малениким, ты задумываешся о том, что хорошо бы его разделить на некоторые части, при этом очень важно, чтобы данные части были максимально независимы (даже на случай повторного применения). При этом, желательно чтобы эти части имели некоторый законченный вид и могли бы передаваться между разработчиками.
И тут — тебя ждут пляски с бубнами
Для сравнения — если у тебя проект на C#, то этот самый момент является достаточно простым и безболезнынным (скорее даже тривиальным).
P.S. Тот факт, что классы — это отдельные единицы трансляци, не мешает пониманию того, что иногда несколько таких классов требуется
объединить в одну сущность — например: для подсистемы обеспечения работы с Базой Данных.
При этом, важно понимать, что сегодня — твой проект маленький и ты вполне обходишься применением (например) СУБД MySQL.
Завтра,по мере развития проекта и увеличения базы данных, встанет ворпос о переходе — например на ORACLE или MS SQL Server.
Очень важно — сделать так, чтобы изменения при переходе на более мощную СУБД коснулись ТОЛЬКО кодов работы с базой Данных!
То есть, изменения в кодах работы с данными, реализующими концепцию CRUD — https://en.wikipedia.org/wiki/Create,_read,_update_and_delete для применяемой тобой СУБД, а не всего твоего проекта. Другими словами — важна независимость подсистемы БД от всего остального проекта.
Важна также и возможность абстрагирования от подсистемы БД на уровне бизнес-логики и GUI твоей программы.
Именно это обеспечивает такое понятие как "модульность".
Здравствуйте, Максим Рогожин, Вы писали:
МР>Привет!
МР>Объясните, пожалуйста, в чем выражается эта нехватка модульной системы в C++? Все классы в отдельных единицах трансляции обычно находятся. Чего не хватает?
Можно ответить максимально просто
Без модулей при любом самом маленьком изменении заголовка класса требуется перекомпиляция всех единиц трансляции, которые используют этот заголовок. С модулями чаще всего это будет не нужно.
Здравствуйте, AlexGin, Вы писали:
AG>Когда у тебя C++ ный проект разростается и перестаёт быть малениким, ты задумываешся о том, что хорошо бы его разделить на некоторые части, при этом очень важно, чтобы данные части были максимально независимы (даже на случай повторного применения). При этом, желательно чтобы эти части имели некоторый законченный вид и могли бы передаваться между разработчиками.
Так это же обычная библиотека (классов, функций) ?!
AG>Именно это обеспечивает такое понятие как "модульность".
Здравствуйте, RedApe, Вы писали:
RA>Без модулей при любом самом маленьком изменении заголовка класса требуется перекомпиляция всех единиц трансляции, которые используют этот заголовок. С модулями чаще всего это будет не нужно.
Как такое может быть? Если изменился заголовок, то значит изменились и все единицы трансляции, которые используют этот заголовок.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Здравствуйте, AlexGin, Вы писали:
AG>>Когда у тебя C++ ный проект разростается и перестаёт быть малениким, ты задумываешся о том, что хорошо бы его разделить на некоторые части, при этом очень важно, чтобы данные части были максимально независимы (даже на случай повторного применения). При этом, желательно чтобы эти части имели некоторый законченный вид и могли бы передаваться между разработчиками.
МР>Так это же обычная библиотека (классов, функций) ?!
+100500
Да, обычная. Но стандартного (с точки зрения языка C++) метода применнинея библиотекам — нет.
То есть — это может быть только один заголовочник (*.h либо *.hpp файл).
Это может быть *.lib (статическая библиотека) с *.h файлом (файлами).
Пример: boost — там в основном заголовочники,
но есть и статические либы и есть возможность даже динамического использования некоторых либ-ов: https://www.boost.org/doc/libs/1_62_0/boost/dll/shared_library.hpp
Также это может быть *.dll файл (как собственно DLL, так и в виде COM-компонента, исполненного в виде dll).
Что стандартно?
AG>>Именно это обеспечивает такое понятие как "модульность".
МР>Чем оно отличается от понятия библиотеки?
Так определи понятие "библиотека" для C++
Что ты сам понимаешь под этим термином в контексте C++?
Здравствуйте, AlexGin, Вы писали:
AG>Для сравнения — если у тебя проект на C#, то этот самый момент является достаточно простым и безболезнынным (скорее даже тривиальным).
А можете сказать за счет чего в C# этот момент является простым? В C# есть какой-то механизм, который отсутствует в C++? Почему этот механизм не добавят в C++?
Здравствуйте, Максим Рогожин, Вы писали:
AG>>Да, обычная. Но стандартного (с точки зрения языка C++) метода применнинея библиотекам — нет. МР>Т.е. C++'s lack of a module system — это про то, что в стандарте C++ нет понятия библиотека? Правильно?
Договориться они не могут. По уму, действие макросов не должно выходить за пределы модуля. Но то по уму, а вот эмбеддщики считают иначе.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Здравствуйте, AlexGin, Вы писали:
AG>Для сравнения — если у тебя проект на C#, то этот самый момент является достаточно простым и безболезнынным (скорее даже тривиальным).
МР>А можете сказать за счет чего в C# этот момент является простым? В C# есть какой-то механизм, который отсутствует в C++? Почему этот механизм не добавят в C++?
ИМХО именно это, является препятствием (предположу, что всё-таки в преспективе преодолимым) для простого введения такого понятия как "модульность" в состав языка.
Также введению и поддержке модульности для C# и платформы .NET сопсобствует тот факт, что это продукция фактически одного производителя: M$.
Эта продукция закрывает относительно узкие нишевые решения (настольный PC, реже — сервер).
В мире C++ нет единого производителя.
Есть различные компании, производящие: компиляторы, линкеры, отладчики, системы сборки, IDE и библиотеки классов для C++. Тот факт, что C++ это универсальный язык (настольные PC, сервера, мобильные устройства, приборы промышленной автоматики, медицинское оборудование, системы военного назначения и т.д.) также добавляет сложности по аспекту модульности.
Учитывая всё сказанное, принятие единого стандарта на модульность выглядит достаточно непростым решением.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Здравствуйте, RedApe, Вы писали:
RA>>Без модулей при любом самом маленьком изменении заголовка класса требуется перекомпиляция всех единиц трансляции, которые используют этот заголовок. С модулями чаще всего это будет не нужно.
МР>Как такое может быть? Если изменился заголовок, то значит изменились и все единицы трансляции, которые используют этот заголовок.
В существующей системе не может, а в модулях можно сделать, чтобы добавление комментария не вызывала потока перекомпиляции.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Интересную статью нашел:
МР>A Module System for C++
МР>Можете объяснить откуда берется много исполняемого кода в modern C++ headers?
МР>
МР>with modern C++, header files contain lot of (executable) codes.
Здравствуйте, alex_mah, Вы писали:
_>Шаблоны, я так понимаю
Шаблоны и раньше были. Тут про modern C++ говорится.
Кстати, интересно, а как шаблоны в модулях определять? В модуле будет храниться просто заголовочный файл с определением шаблона и этот заголовочный файл будет включаться в те единицы трансляции где инстанциируется шаблон? Т.е. как обычно, с помощью препроцессора? Это уже не соответствует идее модулей.
Здравствуйте, AlexGin, Вы писали:
AG>Да, обычная. Но стандартного (с точки зрения языка C++) метода применнинея библиотекам — нет.
А что, нужен? А зачем? Вообще давно придумали статические и динамические библиотеки, они, конечно, вне стандарта C++, но зачем это втаскивать в него, когда это вещи, зависящие от операционной система?
AG>То есть — это может быть только один заголовочник (*.h либо *.hpp файл). AG>Это может быть *.lib (статическая библиотека) с *.h файлом (файлами).
А вот какая разница? Как удобно разработчику — так и сделано.
(Я считаю, что h/hpp-only оно может быть в чисто темплейтном случае, а если есть нетемплейтный код, его надо выносить в код)
Внутри компании библиотеки должны распространяться в исходных кодах, и пусть каждый проект из них делает то, что им надо — статическую библиотеку, динамическую, напрямую в проект влинковывает.
AG>Также это может быть *.dll файл (как собственно DLL, так и в виде COM-компонента, исполненного в виде dll). AG>Что стандартно?
Стандартно отсутствие стандарта. Как хочешь — так и делай, решай сам, у тебя голова на плечах.
AG>Так определи понятие "библиотека" для C++ AG>Что ты сам понимаешь под этим термином в контексте C++?
У меня рекурсивное определение: библиотека — это группа методов с схожим общим назначением и явно выраженной односторонней зависимостью от других библиотек.
Здравствуйте, Dair, Вы писали:
AG>>Да, обычная. Но стандартного (с точки зрения языка C++) метода применнинея библиотекам — нет.
D>А что, нужен? А зачем? Вообще давно придумали статические и динамические библиотеки, они, конечно, вне стандарта C++, но зачем это втаскивать в него, когда это вещи, зависящие от операционной система?
Вопрос: Стандарт нужен ли и зачем? — скорее риторический. Вот сделают и посмотрим, какие проблемы это решит.
Так, например, если бы то, что реализовано в том же C++11, мне показали лет десять назад, я точно также бы усомнился — надо ли это?
Тем не менее, теперь — охотно пользуюсь, и недоумеваю — как ранее работали без этого
AG>>То есть — это может быть только один заголовочник (*.h либо *.hpp файл). AG>>Это может быть *.lib (статическая библиотека) с *.h файлом (файлами).
D>А вот какая разница? Как удобно разработчику — так и сделано.
Здесь подразумевается два разработчика — разработчик библиотечного модуля и разработчик продукта, в котором применяется данный модуль.
То, что удобно первому, далеко не всегда удобно второму и наоборот.
D>(Я считаю, что h/hpp-only оно может быть в чисто темплейтном случае, а если есть нетемплейтный код, его надо выносить в код)
+100500
В то же время, теперь часто в h/hpp выносят много кодогенерации (мне самому это не нпавится, но приходиться мириться).
D>Внутри компании библиотеки должны распространяться в исходных кодах, и пусть каждый проект из них делает то, что им надо — статическую библиотеку, динамическую, напрямую в проект влинковывает.
Смотря в какой компании. Здесь всё зависит от принятой в компании политики. Далеко не всегда одно подразделение делится исходниками с другим.
AG>>Также это может быть *.dll файл (как собственно DLL, так и в виде COM-компонента, исполненного в виде dll). AG>>Что стандартно?
D>Стандартно отсутствие стандарта. Как хочешь — так и делай, решай сам, у тебя голова на плечах.
Так зачем мы сегодня с радостью пользуемся всм тем, что привнесли в нашу жизнь стсандарты C++11/14, зачем изучаем C++17?
Давай писать на C++ образца 1983 года...
AG>>Так определи понятие "библиотека" для C++ AG>>Что ты сам понимаешь под этим термином в контексте C++?
D>У меня рекурсивное определение: библиотека — это группа методов с схожим общим назначением и явно выраженной односторонней зависимостью от других библиотек.
Определение вроде вполне логичное, однако не полное — в плане отсутствия каких-либо упоминаний об интеграции с остальной (скажем так "клиентской") частью системы.
Здравствуйте, Dair, Вы писали:
D>(Я считаю, что h/hpp-only оно может быть в чисто темплейтном случае, а если есть нетемплейтный код, его надо выносить в код)
Ага, и link time code generation.
Иначе как ты будешь оптимизировать инлайновые функции?
Модуль реализуется примерно так — есть файл (или несколько файлов) с интерфейсной частью и частью реализации — после компиляции получается интерфейсный файл с зафиксированным форматом, который хранит интерфейсную часть модуля, и старый добрый объектный файл.
Так тоньше можно регулировать межмодульные зависимости, поскольку зависят теперь имена, а не файлы, можно точно указать какие имена импортируются и какие экспортируются. В случае с хидерами же все имена включённые в хидер теперь экспортируются, в результате количество зависимостей растёт как снежный ком. Это сильно влияет на время компиляции.
Для полной компиляции существенного выигрыша вроде быть не должно, однако я видел лекцию, где модули показали двукратное преимущество, а это неплохо, на мой взгляд. Но основное ускорение будет на инкрементальных билдах.
Далее, можно сильно уменьшить гранулярность импорта, поскольку сделать import xxx гораздо дешевле, чем #include xxx.h. В частности, STL предлагается поставлять примерно так <stl>, <stl/regex> и всё.
Далее, можно избавится от непортируемых и неудобных precompiled headers, unity builds итд.
Далее, поскольку фаза компиляции идёт строго после фазы препроцессинга, отловить циклические включения хидеров компилятором невозможно, а при использовании модулей — запросто.
Далее, кажется решили, что теперь имена в секции реализации имеют internal linkage — уменьшается кол-во нарушений ODR.
Ну и рискну предположить, что для чисто модульных проектов со временем введут запрет на circular dependencies и ODR violations, или вендоры их реализуют сами.
Далее, code completion tools — скорость их работы должна возрасти драматически, поскольку у них есть в наличии файлы интерфейса, а при изменении файла надо будет перепарсить только его.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Привет!
МР>Объясните, пожалуйста, в чем выражается эта нехватка модульной системы в C++? Все классы в отдельных единицах трансляции обычно находятся. Чего не хватает?
Здравствуйте, swingus, Вы писали:
S>Модуль реализуется примерно так — есть файл (или несколько файлов) с интерфейсной частью и частью реализации — после компиляции получается интерфейсный файл с зафиксированным форматом, который хранит интерфейсную часть модуля, и старый добрый объектный файл.
Спасибо! А можете объяснить как шаблоны будут храниться в модулях? Все равно будет текстовое (с помощью препроцессора) включение заголовочного файла с шаблоном во все единицы трансляции, где этот шаблон используется, и каждый раз шаблон будет компилироваться и будет генерироваться инстанциация шаблона?
Шаблоны так и будут храниться в интерфейсном файле и инстанцироваться в точке специализации. Но не включение препроцессором, а импорт, который недорого стоит. Я не знаю текущее состояние дел, но несколько лет назад проскакивали такие варианты:
— хранить полные специализации шаблонов на типах параметров, которые известны в точке объявления шаблона в объектном файле, соответствующем модулю, где объявлен шаблон, а остальные — в объектном файле, где шаблон инстанцирован;
— иметь некий глобальный относительно проекта кэш шаблонных специализаций.
В общем разумно предположить, что количество повторных инстанцирований будет уменьшено или сведено к нулю.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Спасибо! А можете объяснить как шаблоны будут храниться в модулях? Все равно будет текстовое (с помощью препроцессора) включение заголовочного файла с шаблоном во все единицы трансляции, где этот шаблон используется, и каждый раз шаблон будет компилироваться и будет генерироваться инстанциация шаблона?