В C++20 представлены модули — современное решение, которое превращает библиотеки и программы C++ в компоненты. Модуль — это набор файлов исходного кода, которые компилируются независимо от исходных файлов (или, точнее, от единиц перевода, которые их импортируют). Модули устраняют или устраняют многие проблемы, связанные с использованием файлов заголовков.
Они часто сокращают время компиляции. Макросы, директивы препроцессора и неэкспортированные имена, объявленные в модуле, не видны за пределами модуля. Они не влияют на компиляцию единицы перевода, которая импортирует модуль. Модули можно импортировать в любом порядке, не беспокоясь о переопределениях макросов.
Объявления в блоке импортируемого преобразования не участвуют в разрешении перегрузки или поиске имен в импортированном модуле. После компиляции модуля один раз результаты сохраняются в двоичном файле, в котором описываются все экспортированные типы, функции и шаблоны. Компилятор может обрабатывать этот файл гораздо быстрее, чем файл заголовка. Кроме того, компилятор может повторно использовать его во всех местах импорта модуля в проекте.
Как найти модуль числа Python
Модули можно использовать параллельно с файлами заголовков. Исходный файл C++ может import содержать модули, а также #include файлы заголовков. В некоторых случаях файл заголовка можно импортировать в виде модуля, что быстрее, чем использование #include для его обработки с помощью препроцессора. Рекомендуется как можно больше использовать модули в новых проектах, а не файлы заголовков.
Для более крупных существующих проектов, которые активно разрабатываются, поэкспериментируйте с преобразованием устаревших заголовков в модули. Основывайте свое внедрение на том, получаете ли вы существенное сокращение времени компиляции.
Включение модулей в компиляторе Microsoft C++
Начиная с Visual Studio 2022 версии 17.1 стандартные модули C++20 полностью реализованы в компиляторе Microsoft C++.
До того, как он был указан стандартом C++20, корпорация Майкрософт имела экспериментальную поддержку модулей. Компилятор также поддерживает импорт готовых модулей стандартной библиотеки, описанных ниже.
Начиная с Visual Studio 2022 версии 17.5 импорт стандартной библиотеки в виде модуля является стандартизованным и полностью реализован в компиляторе Microsoft C++. В этом разделе описывается старый экспериментальный метод, который по-прежнему поддерживается. Сведения о новом стандартизованном способе импорта стандартной библиотеки с помощью модулей см. в статье Импорт стандартной библиотеки C++ с помощью модулей.
Функцию модулей можно использовать для создания односекционных модулей и импорта модулей стандартной библиотеки, предоставляемых корпорацией Майкрософт. Чтобы включить поддержку модулей стандартной библиотеки, выполните компиляцию с /experimental:module помощью и /std:c++latest . В проекте Visual Studio щелкните правой кнопкой мыши узел проекта в Обозреватель решений и выберите свойства. В раскрывающемся списке Конфигурация выберите Все конфигурации, а затем выберите Свойства> конфигурацииC/C++>Язык>Включить модули C++ (экспериментальные).
Обзор модулей программы
Модуль и код, который его использует, должны быть скомпилированы с теми же параметрами компилятора.
Использование стандартной библиотеки C++ в качестве модулей (экспериментальная)
В этом разделе описывается экспериментальная реализация, которая по-прежнему поддерживается. Новый стандартизированный способ использования стандартной библиотеки C++ в качестве модулей описан в разделе Импорт стандартной библиотеки C++ с помощью модулей.
Импорт стандартной библиотеки C++ в виде модулей вместо ее включения в файлы заголовков позволяет ускорить компиляцию в зависимости от размера проекта. Экспериментальная библиотека разделена на следующие именованные модули:
- std.regex предоставляет содержимое заголовка
- std.filesystem предоставляет содержимое заголовка
- std.memory предоставляет содержимое заголовка
- std.threading предоставляет содержимое заголовков , , , , и
- std.core предоставляет все остальное в стандартной библиотеке C++
Чтобы использовать эти модули, добавьте объявление импорта в начало файла исходного кода. Пример:
import std.core; import std.regex;
Чтобы использовать модули стандартной библиотеки Майкрософт, скомпилируйте программу с /EHsc параметрами и /MD .
Простой пример
В следующем примере показано простое определение модуля в исходном файле с именем Example.ixx . Расширение .ixx требуется для файлов интерфейса модуля в Visual Studio. В этом примере файл интерфейса содержит как определение функции, так и объявление . Однако определения можно также поместить в один или несколько отдельных файлов реализации модуля, как показано в следующем примере. Оператор export module Example; указывает, что этот файл является основным интерфейсом для модуля с именем Example . Модификатор export в f() указывает, что эта функция видна при Example импорте другой программой или модулем. Модуль ссылается на пространство имен Example_NS .
// Example.ixx export module Example; #define ANSWER 42 namespace Example_NS < int f_internal() < return ANSWER; >export int f() < return f_internal(); >>
Файл MyProgram.cpp использует объявление для import доступа к имени, экспортируемого . Example Здесь отображается имя Example_NS , но не все его члены. Кроме того, макрос ANSWER не отображается.
// MyProgram.cpp import Example; import std.core; using namespace std; int main() < cout
Объявление import может отображаться только в глобальных область.
Грамматика модуля
module-name :
module-name-qualifier-seq необ. identifiermodule-name-qualifier-seq :
identifier .
module-name-qualifier-seq identifier .module-partition :
: module-namemodule-declaration :
export Выбрать module module-name module-partition Выбрать attribute-specifier-seq Выбрать ;module-import-declaration :
export Выбрать import module-name attribute-specifier-seq Выбрать ;
export Выбрать import module-partition attribute-specifier-seq Выбрать ;
export Выбрать import header-name attribute-specifier-seq Выбрать ;
Реализация модулей
Интерфейс модуля экспортирует имя модуля и все пространства имен, типы, функции и т. д., составляющие открытый интерфейс модуля. Реализация модуля определяет объекты, экспортированные модулем. В простейшей форме модуль может состоять из одного файла, объединяющего интерфейс модуля и реализацию. Вы также можете поместить реализации в один или несколько отдельных файлов реализации модуля, аналогично тому, как .h используются файлы и .cpp .
Для больших модулей можно разделить части модуля на подмодулы, называемые секциями. Каждая секция состоит из файла интерфейса модуля, который экспортирует имя секции модуля. Секция также может иметь один или несколько файлов реализации секции. Модуль в целом имеет один основной интерфейс модуля, открытый интерфейс модуля, который также может импортировать и экспортировать интерфейсы секций.
Модуль состоит из одной или нескольких единиц модуля. Модуль — это блок преобразования (исходный файл), содержащий объявление модуля. Существует несколько типов единиц модуля:
- Единица интерфейса модуля — это единица модуля, которая экспортирует имя модуля или имя секции модуля. Единица интерфейса модуля содержит export module в объявлении модуля.
- Единица реализации модуля — это модуль, который не экспортирует имя модуля или имя секции модуля. Как следует из названия, он используется для реализации модуля.
- Основная единица интерфейса модуля — это единица интерфейса модуля, которая экспортирует имя модуля. В модуле должна быть только одна основная единица интерфейса модуля.
- Единица интерфейса секции модуля — это единица интерфейса модуля, которая экспортирует имя секции модуля.
- Единица реализации секции модуля — это единица реализации модуля, которая имеет имя секции модуля в объявлении модуля, но не export ключевое слово.
Ключевое слово export используется только в файлах интерфейса. Файл реализации может быть import другим модулем, но не export может содержать никаких имен. Файлы реализации могут иметь любое расширение.
Модули, пространства имен и поиск, зависящий от аргументов
Правила для пространств имен в модулях такие же, как и в любом другом коде. При экспорте объявления в пространстве имен включающее пространство имен (за исключением неэкспортированных членов) также неявно экспортируется. Если пространство имен экспортируется явным образом, экспортируются все объявления в этом определении пространства имен.
При поиске разрешений перегрузки, зависящих от аргументов, в блоке импорта преобразования компилятор рассматривает функции, объявленные в той же единице преобразования (включая интерфейсы модуля), в которых определен тип аргументов функции.
Секции модулей
Раздел модуля аналогичен модулю, за исключением того, что он совместно владеет всеми объявлениями во всем модуле. Все имена, экспортированные файлами интерфейса секционирования, импортируются и повторно экспортируются основным файлом интерфейса. Имя секции должно начинаться с имени модуля, за которым следует двоеточие. Объявления в любой из секций отображаются во всем модуле.
Во избежание ошибок с одним определением (ODR) не требуется никаких особых мер предосторожности. Можно объявить имя (функцию, класс и т. д.) в одной секции и определить его в другой. Файл реализации секции начинается следующим образом:
module Example:part1;
Файл интерфейса секции начинается следующим образом:
export module Example:part1;
Чтобы получить доступ к объявлениям в другой секции, раздел должен импортировать его, но он может использовать только имя секции, а не имя модуля:
module Example:part2; import :part1;
Основной блок интерфейса должен импортировать и повторно экспортировать все файлы разделов интерфейса модуля следующим образом:
export import :part1; export import :part2; .
Основной блок интерфейса может импортировать файлы реализации секций, но не может экспортировать их. Эти файлы не могут экспортировать какие-либо имена. Это ограничение позволяет модулю сохранять сведения о реализации внутри модуля.
Модули и файлы заголовков
Вы можете включить файлы заголовков в исходный файл модуля, поместив директиву #include перед объявлением модуля. Эти файлы считаются фрагментом глобального модуля. Модуль может видеть только имена в фрагменте глобального модуля, которые находятся в заголовках, которые он явно включает. Фрагмент глобального модуля содержит только используемые символы.
// MyModuleA.cpp #include «customlib.h» #include «anotherlib.h» import std.core; import MyModuleB; //. rest of file
Для управления импортируемыми модулями можно использовать традиционный файл заголовков:
// MyProgram.h import std.core; #ifdef DEBUG_LOGGING import std.filesystem; #endif
Импортированные файлы заголовков
Некоторые заголовки достаточно автономны, что их можно использовать с помощью import ключевое слово. Main разница между импортированным заголовком и импортированным модулем заключается в том, что все определения препроцессора в заголовке отображаются в программе импорта сразу после инструкции import .
import ; import «myheader.h»;
Источник: learn.microsoft.com
Модульное программирование
Модуль – это последовательность логически связанных фрагментов, оформленных как отдельная часть программы.
К модулю предъявляются следующие требования:
1) модуль должен реализовывать единственную функцию, т.е. при построении модуля используется концепция: «один модуль – одна функция». Таким образом, модуль – это элемент программы, выполняющий самостоятельную задачу. На его входе он может получать определенный набор исходных данных, обрабатывать их в соответствии с заданным алгоритмом и возвращать результат обработки, т.е. реализуется стандартный принцип IPO (Input – Process – Output) – вход-процесс-выход;
2) на модуль нужно ссылаться с помощью его имени. Он должен иметь один вход и один выход, что гарантирует замкнутость модуля и упрощает сопровождение программ;
3) модуль должен иметь функциональную завершенность, т.е. выполнять перечень регламентированных операций для реализации каждой отдельной функции в полном составе, достаточных для завершения начатой обработки;
4) модуль должен возвращать управление в точку его вызова, в свою очередь, он должен иметь возможность сам вызывать другие модули;
5) модуль не должен сохранять историю своих вызовов и использовать ее при своем функционировании;
6) модуль должен иметь логическую независимость, т.е. результат работы программного модуля зависит только от исходных данных, но не зависит от работы других модулей;
7) модуль должен иметь слабые информационные связи с другими программными модулями – обмен информацией между модулями должен быть по возможности минимизирован;
8) модуль должен быть сравнительно невелик, т.е. быть обозримым по размеру и сложности. Опытные программисты рекомендуют его размер не более двух страниц распечатки на принтере.
Для достижения независимости модулей часто используется принцип информационной локализованности, который состоит в том, что вся информация о структуре данных, о прототипах функций, констант и т.д. сосредотачивается («упрятывается») в отдельном модуле. Доступ к этой информации осуществляется только через этот модуль (в алгоритмическом языке С/С++ такие модули имеют расширение *.h).
Программирование с использованием модулей называется модульным программированием. Оно возникло еще в начале 60-х годов XX в. Модульное программирование основано на идее использования уровней абстракции, когда вся проблема или комплекс задач разбивается на задачи, подзадачи, абстрагируется и представляется в виде иерархического дерева связанных между собой модулей, в совокупности представляющих создаваемое программное обеспечение (ПО).
Достоинствами модульного программирования является следующее:
· большую программу могут писать одновременно несколько программистов, что позволяет раньше закончить задачу;
· можно создавать библиотеки наиболее употребительных модулей;
· упрощается процедура загрузки в оперативную память большой программы, требующей сегментации;
· появляется много естественных контрольных точек для отладки проекта;
· проще проектировать и в дальнейшем модифицировать программы.
Недостатки модульного программирования заключаеются в следующем:
· возрастает размер требуемой оперативной памяти;
· увеличивается время компиляции и загрузки;
· увеличивается время выполнения программы;
· довольно сложными становятся межмодульные интерфейсы.
Модульное программирование реализуется через модули – функции. Функция – это область памяти, выделяемая для сохранения программного кода, предназначенного для выполнения конкретной задачи. Другими словами, функция – минимальный исполняемый модуль программы на языке С/С++. По умолчанию функция имеет тип external, и доступ к ней возможен из любого файла программы. Но она может быть ограничена спецификатором класса памяти static.
Функция характеризуется типом, областью действия связанного с функцией имени, видимостью имени функции, типом связывания.
Все функции имеют рекомендуемый стандартами языка единый формат определения. Он имеет заголовок функции, в котором задаются: тип, имя функции и спецификация формальных параметров:
Тип имя_функции (спецификация_параметров) тело_функции
Тип – это тип возвращаемого функцией значения, в том числе void (кроме типов массива или функции). Умолчанием является тип int. Если тип возврата функции не void, то тело функции должно содержать как минимум один оператор return.
Имя_функции – идентификатор, с помощью которого можно обратиться к функции. Он выбирается программистом произвольно и не должен совпадать со служебными словами и с именами других объектов программы. Однако любая программа на языке С/С++ должна иметь хотя бы одну функцию с именем main – главную функцию, содержащую точку входа в программу.
Спецификация_параметров – список формальных параметров, т.е. переменных, принимающих значения, передаваемые функции при ее вызове. Список формальных параметров перечисляется через запятую. Каждый формальный параметр должен иметь следующий формат:
Тип может быть встроенным (int, long, float, double и т.д.), структурой (struct), объединением (union), перечислением (enum), указателями на них или на функции или классы (class). Имя_формального_параметра представляет собой имя используемой в теле функции переменной. Идентификаторы формальных параметров не могут совпадать с именами локальных переменных, объявленных внутри тела функции.
Объявление формального параметра может содержать инициализатор, то есть выражение, которое должно обеспечить параметру присвоение начального значения. Инициализатор параметра не является константным выражением. Начальная инициализация параметров происходит не на стадии компиляции (как, например, выделение памяти под массивы), а непосредственно в ходе выполнения программы.
В языке С/C++ допустимы функции, количество параметров у которых при компиляции функции не фиксировано, следовательно, остаются неизвестными и их типы. Количество и типы параметров таких функций становятся известными только при их вызове, когда явно задан список фактических параметров. При определении и описании таких функций со списками параметров неопределенной длины спецификацию формальных параметров следует закончить запятой и многоточием.
Каждая функция с переменным количеством параметров должна иметь хотя бы один обязательный параметр. После списка обязательных параметров ставится запятая, а затем многоточие, извещающее компилятор, что дальнейший контроль соответствия количества и типов параметров при обработке вызова функции проводить не нужно.
Спецификация_параметровможет отсутствовать, то есть скобки могут быть пустыми, но в этом случае рекомендуется указывать тип void.
Тело_функции – часть определения функции, ограниченная фигурными скобками и непосредственно размещенная вслед за заголовком функции. Тело_функцииможет быть либо составным оператором, либо блоком. Например:
Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:
Источник: studopedia.ru
Модульные программы, модули и их свойства.
Модулем называют автономно компилируемую программную единицу. Термин «модуль» традиционно используется в двух смыслах. Первоначально, когда размер программ был сравнительно невелик, и все подпрограммы компилировались отдельно, под модулем понималась подпрограмма, т. е. последовательность связанных фрагментов программы, обращение к которой выполняется по имени. Со временем, когда размер программ значительно вырос, и появилась возможность создавать библиотеки ресурсов: констант, переменных, описаний типов, классов и подпрограмм, термин «модуль» стал использоваться и в смысле автономно компилируемый набор программных ресурсов.
Данные модуль может получать и/или возвращать через общие области памяти или параметры.
Первоначально к модулям (еще понимаемым как подпрограммы) предъявлялись следующие требования:
- отдельная компиляция;
- одна точка входа;
- одна точка выхода;
- соответствие принципу вертикального управления;
- возможность вызова других модулей;
- небольшой размер (до 50-60 операторов языка);
- независимость от истории вызовов;
- выполнение одной функции.
Со временем, когда основные требования структурного подхода стали поддерживаться языками программирования, и под модулем стали понимать отдельно компилируемую библиотеку ресурсов, требование независимости модулей стало основным.
Практика показала, что чем выше степень независимости модулей, тем:
- легче разобраться в отдельном модуле и всей программе и, соответственно, тестировать, отлаживать и модифицировать ее;
- меньше вероятность появления новых ошибок при исправлении старых или внесении изменений в программу, т. е. вероятность появления «волнового» эффекта;
- проще организовать разработку программного обеспечения группой программистов и легче его сопровождать.
Таким образом, уменьшение зависимости модулей улучшает технологичность проекта. Степень независимости модулей (как подпрограмм, так и библиотек) оценивают двумя критериями: сцеплением и связностью.
Сцепление и связность модулей.
Сцепление является мерой взаимозависимости модулей, которая определяет, насколько хорошо модули отделены друг от друга. Модули независимы, если каждый из них не содержит о другом никакой информации. Чем больше информации о других модулях хранит модуль, тем больше он с ними сцеплен. Различают пять типов сцепления модулей:
- по данным;
- по образцу;
- по управлению;
- по общей области данных;
- по содержимому.
Сцепление по данным предполагает, что модули обмениваются данными, представленными скалярными значениями. Сцепление по образцу предполагает, что модули обмениваются данными, объединенными в структуры. При сцеплении по управлению один модуль посылает другому некоторый информационный объект (флаг), предназначенный для управления внутренней логикой модуля.
Сцепление по общей области данных предполагает, что модули работают с общей областью данных. В случае сцепления по содержимому один модуль содержит обращения к внутренним компонентам другого (передает управление внутрь, читает и/или изменяет внутренние данные или сами коды), что полностью противоречит блочно-иерархическому подходу. Отдельный модуль в этом случае уже не является блоком («черным ящиком»): его содержимое должно учитываться в процессе разработки другого модуля.
Связность – мера прочности соединения функциональных и информационных объектов внутри одного модуля. Если сцепление характеризует качество отделения модулей, то связность характеризует степень взаимосвязи элементов, реализуемых одним модулем. Различают следующие виды связности (в порядке убывания уровня):
- функциональную;
- последовательную;
- информационную (коммуникативную);
- процедурою;
- временную;
- логическую;
- случайную.
При функциональной связности все объекты модуля предназначены для выполнения одной функции. При последовательной связности функций выход одной функции служит исходными данными для другой функции. Информационно связанными считают функции, обрабатывающие одни и те же данные. Процедурно связаны функции или данные, которые являются частями одного процесса.
Временная связность функций подразумевает, что эти функции выполняются параллельно или в течение некоторого периода времени. Логическая связь базируется на объединении данных или функций в одну логическую группу. В том случае, если связь между элементами мала или отсутствует, считают, что они имеют случайную связность.
Источник: studfile.net