LINUX.ORG.RU

А знаете как деплоить C++20 modules с библиотекой?

 ,


0

4

А никак.

В доках GCC (ссылку не дам, уже потерял) прямым текстом сказано, что CMI (Compiled Module Interface) – формат не портабельный и может использоваться только для кеширования в процессе сборки. Он может меняться даже в зависимости от версии компилятора и целевой архитектуры, его смысл и scope примерно тот же что у precompiler headers. Что в общем естественно: компилятор сериализует туда свои внутренние структуры.

Следовательно, деплоить интерфейс модуля можно исключительно исходником. Т.е. если раньше было .h + .cpp, то теперь вместо .h у нас будет какой-нибудь .cppm – интерфейс модуля, который тоже надо деплоить вместе с либой, а в сорцах опять два исходника на модуль: .cppm + .cpp. Что поменялось? Расширение интерфейсного файла. Который теперь ещё и замусорен новыми кейвордами export и import.

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

Тьфу. Ни одну фичу не могут сразу нормально и ДО КОНЦА проработать.

★★★★★

Последнее исправление: dimgel (всего исправлений: 4)

Ответ на: комментарий от hobbit
  1. И почему же?

  2. А нафига тогда вообще модули?

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

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от dimgel

Лучше в D. Он под FreeBSD есть.

И интерфейсные файли там не обязательны:

But they are not required in the way that C++ header files are, and they are not part of the D language. They are a feature of the compiler, and serve only as an optimization of the build process.
apt_install_lrzsz ★★★
()
Ответ на: комментарий от apt_install_lrzsz

Не так давно на ЛОРе кто-то написал, что C++20 сделал D окончательно не нужным. Мой заглавный пост этому несколько противоречит, но:

  1. Я про D знаю только две вещи: (1) его популярность на уровне статпогрешности, (2) его стандартная либа юзает GC, который хоть и можно дропать после любого вызова, идея в целом мне не нравится.

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

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от dimgel

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

Это сишка или вообще асм. Иначе ты какой-то неправильный перфекционист.

slackwarrior ★★★★★
()
Ответ на: комментарий от dimgel

его популярность на уровне статпогрешности

его стандартная либа юзает GC

Что есть, то есть. И это огорчает. А ведь gdc даже в gcc был добавлен:

GDC was merged into the upstream GCC tree in GCC 9.0 and continues to be developed and distributed as part of GCC.

А я бы лучше на работе доп. ПО писал на нём, а не на питоне.

apt_install_lrzsz ★★★
()
Ответ на: комментарий от apt_install_lrzsz

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

dimgel ★★★★★
() автор топика
Ответ на: комментарий от rupert

В шланге по слухам уже давно есть поддержка std modules, в gcc ещё нет, в стандарте она будет в C++23. Но это нерелевантно: компиляторо-специфичный CMI-кеш std-модуля может обновляться вместе с компилятором. В отличие от модулей простых смертных.

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от dimgel

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

apt_install_lrzsz ★★★
()
Ответ на: комментарий от apt_install_lrzsz

Что может быть полезно, если освобождение ресурса в каком-то случае операция не быстрая.

GC – он не про произвольные ресурсы, а про память. Например в жаве, которая с GC, файлы приходится закрывать явно внутри finally{}, т.к. из-за GC он не закрывается сам при выходе из scope, а оставлять его открытым на неопределённый срок, пока GC до него доберётся и вызовет finalize() – это неряшливость.

Отсюда вывод: RAII и GC – взаимно-исключающие концепции. Обе реально удобные (сейчас мне GC на фиг не нужен, но в своё время меня пропёрло), и выбор – дело вкуса. В байтодрочерском стиле кодирования рулит RAII.

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 4)
Ответ на: комментарий от apt_install_lrzsz

можно заставить освобождать ресурсы

Что ты под этим подразумеваешь? Освобождение памяти или выполнение кода деструктора?

Память быстро освобождается. Там аллокаторы давно задрочены на эффективность. Не нравится стандартный - можно вонзить какой-нибудь jemalloc.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Что ты под этим подразумеваешь? Освобождение памяти или выполнение кода деструктора?

Второе, про деструктор.

@dimgel

Я имею ввиду именно finally. Например, когда нужно провести ресурсоемкие вычисления или realtime какое, насоздавав по ходу дела объектов, а поудалять их с выполнением кода из их finally потом отдельно, когда realtime уже закончилось. Запустив для этого обратно GC и более не заморачиваясь.

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

apt_install_lrzsz ★★★
()
Ответ на: комментарий от apt_install_lrzsz

Depends. Примеры вариантов (не утверждаю, что исчерпывающие):

  1. Деструкторы объектов тяжёлые (закрывают внешние ресурсы) – тогда высвобождение памяти на их фоне будет несущественным.

  2. Деструкторы вырожденные – можно заюзать мой любимый аллокатор «arena», который аллоцирует все объекты в одном большом блоке памяти, а в конце высвобождает всю память одним вызовом, не вызывая деструкторы вообще.

  3. PHP style: на кой чёрт заморачиваться с закрытием ресурсов и высвобождением памяти, если после завершения скрипта программы система сама всё приберёт.

  4. RAII вообще помещает всё что может на стек, и стоимость освобождения памяти строго равна нулю.

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 2)
Ответ на: комментарий от apt_install_lrzsz

поудалять их с выполнением кода из их finally потом отдельно, когда realtime уже закончилось.

Мой предыдущий ответ вообще не в тему. Ну да ладно. Вот это процитированное поведение делается элементарно например так: складируем умные указатели на все создаваемые объекты во что-то типа vector<unique_ptr<any>> (что должно быть вместо any – хз, но думаю это решаемо), а потом отложенно этот vector чистим. Можно и арену написать так, чтобы она деструкторы вызывала (полагаю, это будет +24 байта на каждый аллоцируемый объект), и уничтожать её отложенно.

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 6)
Ответ на: комментарий от James_Holden

Хотелка в том, чтобы исходники деплоить не надо было вообще – ни .h, ни .cppm. Как в жаве: .jar-архив содержит скомпилятые .class-файлы, из которых javac эффективно выковыривает интерфейс. А в плюсах деплоют .a-архив с портабельными .o-файлами, а могли бы в довесок к ним в этот же архив запихать и портабельные CMI.

Вот только портабельными CMI никто не озаботился, а без них эти «C++20 modules» – профанация, т.к. по-прежнему требует деплоймента интерфейса в исходниках, содержащих в т.ч. детали реализации.

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от dimgel

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

И плюсы. Ты или крестик сними…
По теме - все уже давно на LLVM и Clang. И да, .Net выигрывает.

Oldboy
()

А никак.

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

то есть общий промежуточный формат вообще не нужен.

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

а зачем?

Тыц, тыц.

твои интерфейсы модулей будут однократно (если они не меняются) скомпилированы на месте сборки

И чем это отличается от PCH, нафига надо было модули городить?

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 2)
Ответ на: комментарий от dimgel

т.к. по-прежнему требует деплоймента интерфейса в исходниках

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

Логично предположить, что авторы «модулей С++20» делали их для С++20, а не для C++98. Как С++20 будет отделяться интерфейс от реализации, не очень понятно.

James_Holden ★★★
()
Ответ на: комментарий от dimgel

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

-от порядка импортов ничто не зависит

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

ps. модули вообще-то не для ускорения компиляции, это чисто бонус. они для строгой изоляции кусков ПО, и их взаимодействия через набор деклараций, никак не зависящего от контекста использования.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 2)
Ответ на: комментарий от alysnix
  • в подавляющем большинстве моих .h-файлов нет никаких дефайнов. они «железно заморожены» не менее, чем описываемый тобой гипотетический интерфейс модуля. именно поэтому его можно скомпилировать в PCH только раз (если не меняются те интерфейсы, что он инклудит, разумеется).

  • от порядка инклудов у нормальных людей тоже никогда ничего не зависело (вспоминается только что <windows.h> нужно инклудить первым, но это когда было).

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

Ещё раз:

И чем это отличается от PCH, нафига надо было модули городить?

dimgel ★★★★★
() автор топика
Ответ на: комментарий от dimgel

Как в жаве: .jar-архив содержит скомпилятые .class-файлы, из которых javac эффективно выковыривает интерфейс

И эта возможность, кстати, автоматически тянет за собой возможность восстановления человекочитаемой реализации из class файла и невозможность ее скрытия (разве что обфускацией).

James_Holden ★★★
()
Ответ на: комментарий от dimgel

складируем умные указатели на все создаваемые объекты во что-то типа

Да, полагаю, такое можно применить. С другой стороны, кое-кто счёл бы такое решение костылём. А а авторы кое-каких языков решили бы, что пользователям их языка (и всего сопутствующего инструментария, само собой) совершенно излишне заморачиваться решением подобных вопросов. Но вот зачем к D изначально жёстко прикрутили GC (а потом решили, что надо оторвать, а то режим betterC уныл), я так и не понял.

apt_install_lrzsz ★★★
()
Ответ на: комментарий от dimgel

Если надо прятать реализацию - то .h + .a это делает, а java не очень.

Если не надо прятать, а надо просто все в одном файле - то можно было бы просто в .a добавлять .h файл, тут модули по сути не нужны.

James_Holden ★★★
()
Ответ на: комментарий от apt_install_lrzsz

С другой стороны, кое-кто счёл бы такое решение костылём.

Вопрос философский. У меня таких кейсов не было никогда, а редкому кейсу – редкий костыль. :) Или, более нейтрально: в каждой области свои паттерны. И кстати, употреблять слова realtime и GC в одном каменте – это ль не костыль и моветон?

dimgel ★★★★★
() автор топика
Ответ на: комментарий от dimgel

в подавляющем большинстве моих .h-файлов нет никаких дефайнов. они «железно заморожены» не менее, чем описываемый тобой гипотетический интерфейс модуля. именно поэтому его можно скомпилировать в PCH только раз (если не меняются те интерфейсы, что он инклудит, разумеется).

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

одна незакрытая фигурная скобка в хидере чего стоит.

программеры одиночки вообще не рассматриваются индустрией как производственная единица.

alysnix ★★★
()
Ответ на: комментарий от dimgel

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

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

apt_install_lrzsz ★★★
()
Ответ на: комментарий от James_Holden

Если надо прятать реализацию - то .h + .a это делает, а java не очень.

Цель – не обфускация, а отсутствие необходимости перекомпилять пользовательский код, если в библиотечном .h поменялась только реализация. Не помню насчёт maven (java), но вот sbt (scala) в этом плане – был изначально умный: он не пересобирает зависимые от изменившегося class-файла, если public API этого class-файла не поменялся. Он был вынужден быть умным, т.к. scala дьявольски медленно компиляется.

Теперь про плюсы. Как называется этот паттерн, используемый в т.ч. в Qt – когда API-классы содержат указатель на классы-реализации? Вот модули должны были убрать необходимость в этом костыле (об этой цели писали все). Потому что CMI содержал бы не ВСЕ подряд поля/методы класса, а только export-ed. А экспорт интерфейса модуля в виде текстового исходника эту проблему не решает, т.к. в текстовом исходнике, как и прежде, будут задекларированы ВСЕ поля и методы класса, а не только export-ed.

dimgel ★★★★★
() автор топика

Уже много лет как знаем. Никто и не скрывал, что это не про пакетизацию, а средство инкапсуляции и оптимизации. Замена заголовкам, по сути. Так что недоработкой это не является.

xaizek ★★★★★
()
Ответ на: комментарий от dimgel

Потому что CMI содержал бы не ВСЕ подряд поля/методы класса, а только export-ed.

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

ps… размер и смещение расчитывает разумеется компилятор при компиляции импортирующего модуля.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

в подавляющем большинстве моих .h-файлов нет никаких дефайнов.

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

Ок, т.е. в твоём личном опыте в хидерах дефайны есть. При этом ты топишь за модули, в интерфейсах которых дефайнов не будет. Я так понимаю, они волшебным образом сами исчезнут? Или тебе придётся САМОМУ приводить интерфейсы к человеческому виду? В первом случае у меня вопросов нет: видимо это волшебство – и есть то, что выгодно отличает модули с деплойментом текстового интерфейса от хидеров. А во втором случае я третий и последний раз повторяю вопрос:

И чем это отличается от PCH, нафига надо было модули городить?

dimgel ★★★★★
() автор топика
Ответ на: комментарий от alysnix

размер класса должен быть известен импортеру

Нынешние непортабельные CMI (как и непортабельные PCH) не содержат размер класса? И соответственно в гипотетический портабельный CMI его тоже невозможно запихать?

dimgel ★★★★★
() автор топика
Ответ на: комментарий от dimgel

И чем это отличается от PCH, нафига надо было модули городить?

еще раз, ты видимо упустил. я дописывал мессагу выше. модули это вообще не про ускорение компиляции. ускорение тут бонус. а pch это просто для ускорения компиляции и его надо ручками делать.

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от dimgel

а отсутствие необходимости перекомпилять пользовательский код, если в библиотечном .h поменялась только реализация

Вот это уже понятнее.

Потому что CMI содержал бы не ВСЕ подряд поля/методы класса, а только export-ed.

А смысл? Надо знать размер объекта, для этого надо знать все поля. Даже если ты как-то побочно всунешь размер - все равно при его изменении придется перекомпилировать пользовательский код. Не понятно что это дает.

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

Если надо избежать перекомпиляции пользовательского кода - всякие фабрики городят, вроде. Что решает проблему.

James_Holden ★★★
()
Ответ на: комментарий от dimgel

Нынешние непортабельные CMI (как и непортабельные PCH) не содержат размер класса? И соответственно в гипотетический портабельный CMI его тоже невозможно запихать?

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

я тебе возражал на идею о «частичном определении класса», то есть показывании только публичных полей… теоретически непубличные методы, невиртуальные можно скрыть, но это уже вопрос к с++. тогда надо иметь семантику «дописывания класса» к его частичному определению в хидере.

alysnix ★★★
()
Ответ на: комментарий от alysnix

модули это вообще не про ускорение компиляции.

  1. Сам придумал?

  2. В этой теме я вообще не говорил про что они, я говорил что реализация недоделанная. Пример доделанной реализации – это жава.

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

По-прежнему не вижу, каким образом. С модулями я трахался достаточно долго (в рамках одного проекта, т.к. см. сабж), и никакой разницы в workflow не заметил: только сущностей в makefile побольше стало. И секса (с module map и корявой генерацией gcc-шных .d-файлов), так что в итоге откатил.

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от James_Holden

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

[… и всё остальное, в т.ч. про шаблоны …]

Так, вот это я упустил. Логично.

@alysnix

я тебе возражал на идею о «частичном определении класса»,

Угу, после вышепроцитированного понял.

Ну, тогда модули вообще не нужны. Твой аргумент про более удобный workflow мне пока непонятен от слова никак.

dimgel ★★★★★
() автор топика
Последнее исправление: dimgel (всего исправлений: 1)