279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
1 | |
Своя библиотека и сборка программ19.06.2020, 16:28. Показов 3309. Ответов 21
Метки нет (Все метки)
Приветствую всех. Есть такая система, как IMB i (раннее название AS/400). Она консольная. Я пишу программы для этой системы на С++. Может кто-то скажет, что правильнее для этой системы писать программы на ее "родном" языке программирования - RPG, но я не хочу разводить здесь об этом полемику.
Итак, для создания на С++ программ в системе есть компилятор и стандартная библиотека. Весь этот набор "чуть-чуть" не дотягивает до стандарта С++11 (то есть, что-то из С++11 присутствует, чего-то еще не реализовали). В процессе создания программ у меня родилось желание написать свою библиотеку классов (не заменитель стандартной). Но возник вопрос, как максимально удобно эту библиотеку использовать. Дело в том, что, по сути, IDE, решающей мою проблему, для этой системы нет. Компиляция и сборка программ происходит так. Из исходного кода создаются модули через командную стоку. И через командную же строку модули собираются в программу. Но если я сделаю свою библиотеку, то, естественно, один ее класс может начать использовать второй, а тот третий и т. д. При этом, если использовать в программе первый класс, то надо отслеживать, какие классы библиотеки он использует и вручную создавать модули этих классов. Не существует ли способа, который позволяет это автоматизировать? У меня есть варианты, но о них после ваших предложений
0
|
19.06.2020, 16:28 | |
Ответы с готовыми решениями:
21
Своя графическая библиотека Своя библиотека Java Своя сборка Delphi 7 Библиотека программ |
1486 / 1413 / 240
Регистрация: 19.02.2010
Сообщений: 3,914
|
|
19.06.2020, 19:21 | 2 |
Рядом с С/С++-транслятором и линкером должен валяться и "библиотекарь". Что-то с "lib" в названии (перед расширением екзешника).
Т.е. откомпилированные двоичные коды (объектники), соответствующие модулям с разными классами, можно соединить в одну библиотеку. Далее линкером (или в написанном make-файле) к рабочему проекту цеплять именно эту библиотеку (всю её целиком, а не отдельными её составляющими модулями). Этим сразу ликвидируется необходимость перечисления всех/отдельных модулей библиотеки в ком.строке линкера или в make-файле каждого нового проекта - нужно будет подцепить только библиотеку, независимо от того, как будет в дальнейшем в проекте меняться её использование (какие модули библиотеки будут реально задействоваться). Модули рабочего проекта при этом могут спокойно дёргать заголовочники отдельных модулей библиотеки, т.е. сводить заголовочники и исходники всей библиотеки в единые общие файлы не надо. Если в системе есть исходники С/С++-библиотек - то там сделано аналогично: 1) Исходники и заголовочники разных модулей живут по-отдельности друг от друга - но сведены в малое число библиотек (одна для С, одна для всего стандартного в С++ - или как-то похоже). 2) К модулям Вашего проекта подключаются заголовочники отдельных модулей стандартных библиотек - но затем линкер собирает исполняемый файл на основе библиотеки как целого. Ну а чтобы один модуль заголовочника тащил за собой всё, что нужно для объявлений в этом заголовочнике - надо просто прописывать межмодульные связи именно через заголовочники. Т.е. если класс Б является потомком класса А - то заголовочник для А должен подключаться не в модуле для Б перед инклюдом заголовочника для Б, а в заголовочнике для Б перед тамошними объявлениями. Для ликвидации возможности повторных включений - в языке есть #ifndef-#define-#endif.
1
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
20.06.2020, 11:22 [ТС] | 3 |
То есть, Вы предлагаете выполнить компиляцию всей библиотеки в один модуль, а затем в каждую программу линкером включать этот модуль, даже если программа использует из библиотеки одну маленькую независимую ни от чего функцию?
0
|
1486 / 1413 / 240
Регистрация: 19.02.2010
Сообщений: 3,914
|
|
20.06.2020, 14:48 | 4 |
Да, именно так.
Ибо это стандартный способ для компилируемых языков. Бояться последующего роста размера екзешника - не надо. Ибо линкер давно уже настолько умный, что тянет из библиотеки (да и из объектников проекта) только реально используемое (т.е. тянет зависимости даже не помодульно - а может брать только отдельные функции/классы из модулей, включающих >1 функции или класса). Если не верите в утверждение из предыдущего предложения - статически соберите любой hello world и сравните размер получившегося екзешника с размером библиотеки С/С++, которая линковалась к этому проекту. Т.е. когда я в предыдущем посте писал слова "к рабочему проекту цеплять именно эту библиотеку (всю её целиком, а не отдельными её составляющими модулями)" - то имел в виду указание только одного имени в командной строке линкера, вместо указания кучи имён библиотечных модулей. Ибо проблема-то у Вас в первую очередь - с отслеживанием зависимостей, какие модули библиотеки нужны некоторому проекту и/или просто друг другу. Вот и надо всю библиотеку собрать упихать в единый библиотечный модуль - и затем им пользоваться. А линкер из такой общей библиотеки просто не будет брать ничего лишнего (ненужного некоторому данному конкретному проекту). В общем, способ сборки библиотеки аналогичен тому, каким Вы собираете любую свою прогу - просто лишь меняется "формат" итоговой "программы" на втором шаге (несколько ранее (на первом шаге) откомпилированных по-отдельности друг от друга модулей здесь собираются не линкером в единый екзешник - а библиотекарем в единый бинарный файл библиотеки). А затем полученная библиотека используется аналогично любой стандартной (Вы же не выбираете вручную модулей из стандартной библиотеки) при сборке уже реальной программы.
1
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
|
|
20.06.2020, 15:34 | 5 |
0
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
22.06.2020, 16:49 [ТС] | 6 |
Вот тут есть "фокус". Стандартные библиотеки С/С++ не попадают внутрь создаваемых программ ни полностью, ни частично. Использование функций и/или классов этих библиотек осуществляется через так называемые сервисные программы. Сервисная программа - это аналог dll в Windows. То есть, все функции и классы стандартных библиотек С/С++ скомпилированы в некоторое количество сервисных программ. При создании программы сборщик "понимает", что данная функция/класс реализованы в стандартной библиотеке и автоматически подключает к создаваемой программе нужную сервисную.
Мне можно, конечно, сделать аналогично: скомпилировать всю свою библиотеку классов в сервисную программу и при сборке своих программ подключать вручную эту сервисную. Но у этого варианта есть существенный минус. Так как я свою библиотеку активно Хочется надеяться, что в сборщике в моей системе эта возможность есть. Но пока я ее не смог найти. Был проведен эксперимент. Я создал модуль с двумя функциями. Затем в главном модуле я вызвал только одну из этих функций. В итоговой программе средствами ОС было определено, что вторая функция сборщиком не была исключена. Она так и осталась в программе, хотя нигде не используется. Но хочется сказать, что Вы направили меня на путь поиска. Попробую поискать в направлении настроек сборщика, так как вытягивание в готовую программу только реально используемого кода было бы, наверное, идеальным решением. Если вдруг кому-то интересно, то вот ссылка на команду сборки программы, которую я использую.
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
|
|
22.06.2020, 20:44 | 7 |
Охо-хо! Для этого есть тесты!
Вы не пишете тесты на свой код?! Добавлено через 2 минуты Вообще возьмите за правило никогда не трогать API, которое уже пошло в работу. А чтобы это правило строго соблюдалось - пишите тесты, которые будут автоматически прогоняться после каждой компиляции вашей "сервисной программы". Даже если API у вас получилось плохенькое и неудобное, вы его засунули в прод, а потом только это осознали - не трогайте. Напишите новое API, старое объявите deprecated и потихоньку переводите новые версии софта на него. НИКОГДА не ломайте API.
1
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
23.06.2020, 08:08 [ТС] | 8 |
DrOffset, спасибо за совет! Тесты для библиотеки я пишу. Разрешите два вопроса:
1. Тесты должны писаться только для интерфейсных функций и методов или для внутренних тоже (я пока пишу только для интерфейсных)? 2. Какие существую способы объявить функцию или метод deprecated? Только на уровне документации или особой конструкцией кода, приводящей к ошибкам/предупреждениям при компиляции? Что же, зафиксирую первый вариант реализации библиотеки - реализация в виде сервисной программы.
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
|
|
23.06.2020, 11:39 | 9 |
В идеале - для всего.
Но чтобы контролировать неизменность API, достаточно только "интерфейсных", если я конечно вас правильно понял. В С++14 - штатные: https://en.cppreference.com/w/... deprecated До С++14 можно использовать специфичные для компилятора прагмы или атрибуты. Например https://gcc.gnu.org/onlinedocs... butes.html
1
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
24.06.2020, 19:02 [ТС] | 10 |
Тогда еще вопрос по этой теме. Как написать тест для private функции класса? Ведь она не видна при создании объекта класса и я не могу ее вызвать.
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
|
|
24.06.2020, 19:07 | 11 |
d7d1cd, Есть два подхода -
1) private вообще не тестировать 2) тестировать с помощью friend-кейсов. testcase для этого класса делается ему friend-ом и все. чтобы это не маячило в финальной версии кода, это можно делать под специальным макросом. Поищите библиотеки для юнит-тестирования, некоторые предоставляют готовые решения этого вопроса.
1
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
14.08.2020, 08:52 [ТС] | 12 |
Всем привет. Наконец-то нашел время продолжить обсуждение.
Выше было озвучено 2 варианта реализации библиотеки (буду называть ее OLI - Object Library for IBM i): 1. Реализация в виде одного модуля, который будет включаться в каждую программу. 2. Реализация в виде общей сервисной программы, функции которой будут использоваться программами по мере необходимости. При добавлении функционала в OLI первый вариант более удобен в плане поставки в прод: не требуется обновление сервисной программы, как в случае второго варианта. Однако в случае варианта 1 появляется "функциональная избыточность" - в программе может использоваться один класс из OLI, но в модуле будет вся библиотека (я пока не смог найти опций компилятора и(или) сборщика, позволяющих не включать в программу неиспользуемые функции: может быть их вообще не существует...). В первом посте я говорил, что у меня есть свои варианты реализации. На самом деле вариант один. Он заключается в создании программы анализатора-сборщика (буду называть его imake). На вход imake будет подаваться путь к проекту создаваемого приложения. В проекте будет сценарий сборки этого приложения. На первом этапе imake будет анализировать исходные коды проекта и, в случае нахождения там заголовка из OLI, поместит реализацию этого заголовка в отдельный CPP файл. Так же будет рекурсивно проведен анализ самого заголовка и его реализации на случай, если они используют другие части OLI. В итоге процесса анализа вышеуказанный CPP файл наполнится используемым в проекте кодом из OLI. Далее этот файл и исходники проекта imake соберет в модули, а модули - в программу. В результате программа проекта будет содержать только то из OLI, что использует. Конечно, в используемом классе может не использоваться какая-то его часть, но эту оптимизацию я делать не планирую (по крайней мере пока). Прошу вас прокомментировать мой вариант, сделать замечания, дать советы. Заранее признателен!
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
|
|
14.08.2020, 09:05 | 13 |
Она как раз есть во втором варианте и никуда не денется. А в первом (если это статическая библиотека) при правильной декомпозиции на модули (объектные файлы), должна быть возможность (у линкера) не включать неиспользуемые модули с неиспользуемыми функциями.
Добавлено через 8 минут Не понятно почему это так. Обычно при обновлении софта, если он требует новую версию своих зависимостей (dll\сервисных программ), а старую версию никто не использует, то ее можно удалить - что и делают все вменяемые пакетные менеджеры, например. Я могу ошибаться, но мне кажется вы недостаточно провели экспертизы стандартных решений и практик, чтобы приступать к разработке своего варианта. Прикручивание своего собственного препроцессора к сборке, который хитро компонует код, это завышение порога входа в сопровождение вашей программы. Если бы мой подчиненный мне предложил такое, я бы не дал добро до тех пор, пока не удостоверился, что это решение точно оправдано чем-то существенным.
0
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
17.08.2020, 08:30 [ТС] | 14 |
Да, во втором варианте сервисная программа будет содержать в себе все возможности библиотеки. Но так и надо. Ведь сервисная программа - это та же библиотека, только в скомпилированном виде. Она будет одна на всю систему. В Windows же библиотека kernel32.dll одна и содержит в себе много функций, которые каждая конкретная программа не обязана использовать все. Программа использует только нужные ей функции. Так же и у меня во втором варианте.
К сожалению я не нашел в опциях своего линкера такой возможности. Вероятно, ее просто нет. Линкер принимает на вход список модулей и пакует их все в одну программу. Функции модуля могут вообще не использоваться внутри программы, но он все равно будет включен в ее тело. Стандартные (не для моей системы) решения зачастую основаны на том, что линкер в итоге выкинет неиспользуемое из готовой программы. У меня нет такого основания... В моей системе на сегодняшний день стандартные решения - скрипт сборки. Вы бы видели программы в нашей системе . Одну и ту же по сути вещь все программисты делают по-своему. Час разбираешься с кодом, чтобы в конце понять, что он делает ровно то же, что и код, с которым ты тоже час разбирался в другой программе 2 дня назад... Библиотека, надеюсь, хоть как-то уменьшит количество велосипедов. Кроме этого, использование моего препроцессора не является обязательным. Можно использовать сборку по старинке - через скрипты сборок. Просто в этом случае в программе будет вся библиотека, что скажется на ее размере и только. Ну и, по сути, это ручная (читай долгая и далеко не безошибочная) сборка.
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
|
|
19.08.2020, 13:50 | 15 |
Она есть по умолчанию. Если вы правильным образом подготовите объектные файлы внутри библиотеки, то она заработает.
0
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
19.08.2020, 14:25 [ТС] | 16 |
Подскажите, что значит "правильным образом"?
Я пробовал делать следующим образом. Создал 3 исходника: первый содержал функцию main , второй содержал функцию печати строки на экран print , третий - функцию сложения чисел sum . В исходнике с main так же был добавлен прототип print (хедер не делал, так ка это был просто тест) и вызов этой функции. Обращаю внимание, что функцию sum я не вызывал. Далее компилятором из указанных исходников было создано 3 объектных файла (в терминах AS/400 - это модули). Затем модули были собраны в программу (при сборке было указано все 3 модуля).В AS/400 есть команда, которая показывает из каких модулей состоит программа. Так вот моя программа содержала в себе все 3 модуля. Неиспользуемый модуль с функцией sum был так же включен в тело программы...
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
|
|
19.08.2020, 14:32 | 17 |
0
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
19.08.2020, 15:16 [ТС] | 18 |
Исполняемый файл.
Просто для справки: программа - это исполняемый файл, сервисная программа - это аналог динамической библиотеки в Windows (не знаю почему ее в AS/400 тоже называют программой).
0
|
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
|
|
19.08.2020, 16:06 | 19 |
Тогда возможно вы действительно правы и ваш линкер не может этого.
Для уверенности я бы конечно не отказался посмотреть своими глазами на него, но похоже это не особо возможно сделать быстро. Формально это верно потому что. И библиотека и запускаемый файл - это все программы. Но в повседневной жизни лучше это разделять, с этим можно согласиться.
1
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
19.08.2020, 16:23 [ТС] | 20 |
Есть публичный сервер AS/400 - pub400.com. Для получения доступа надо зарегистрироваться.
Для работы в режиме командной строки с ним нужен какой-нибудь терминал 5250. Ну а так же есть справка по командам компиляции и сборки. Справка по команде компиляции C++ кода (запуск компилятора) здесь, справка по команде сборки (запуск линкера) тут. Буду очень признателен, если хотя бы посмотрите справку. Может я не выставил нужных опций просто...
0
|
19.08.2020, 16:23 | |
19.08.2020, 16:23 | |
Помогаю со студенческими работами здесь
20
Не подключается DLL библиотека (не распознается сборка) Кастомная сборка программ библиотека на масм32 для программ высокого уровня Библиотека для запуска программ и работы с окнами Сборка для программ Adobe до 30к Инструменты разработчика Access. Библиотека программ, надстроек и справочного материала Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |