[Autumn Series] Модули

Отговори
Потребителски аватар

Автор на темата
Autumn Shade EA\DICE
Developer
Developer
Мнения: 138
Регистриран: 14 мар 2019, 13:20
Се отблагодари: 20 пъти
Получена благодарност: 53 пъти

[Autumn Series] Модули

Мнение от Autumn Shade EA\DICE » 09 май 2019, 12:28

Съдържание:
  • Какво са модулите?
  • Декларация
  • Компилация
  • Предимства
  • Модуларен интерфейсен агрегат и модуларен имплементиращ агрегат
  • Какво следва?
Какво са модулите?
Модулите са едни от няколкото най-силни или да ги наречем важни, нови попълнения в езика. Все още са експериментални, но работят безотказно. Те са в езика C++20. Модулите са доста обещаващи като изпълнение, защото те могат да премахнат ограниченията, които header-ните файлове ни даваха. Например, разделянето на header и source файл като метод/структура е старо колкото самият препроцесор.

В края на краищата, чрез модулите ще имаме по-добър и удобен вариант за организация на кода в различни пакети(packages).По-лесно ще става дистрибуцията на кода, по-бързо ще се компилира самият проект.

Обяснението на модулите откъм гледната точка на потребителите може да изглежда доста лесно, но това няма да се отнася за перспективата на тези, които ги имплементират. Моят план за този пост е да започна с прост пример за модули и да надграждаме функционалността с малки стъпки, докато се чете.

Декларация
Като за начало, нека кажем, че имаме модул amxxbg:

Код: Избери всички

// amxxbg.cppm

export module amxxbg;

export auto print() -> void {
	std::cout << "Print()" << std::endl;
}
Изразът export module amxxbg е декларацията на модула. Като сложим ключовата дума exportпред функцията print, ние казваме, че този модул ще съдържа тази функция в себе си, следователно, функцията ще може да бъде използвана от потребителя на модула.

Код: Избери всички

// main.cpp

import amxxbg;

auto main() {
	
	print();

	return 0;
}
import amxxbg включва модула amxxbg и прави така, че всички функции, съдържащи се в модула, да могат да бъдат използвани в main.cpp . Това е лесната част. Предизвикателството за в момента е да се компилира програмата.

Декларация на модуларните файлове:

Но преди това, забелязахте ли странното име на файла amxxbg.cppm?

Това разширение най-вероятно означава cpp module декларация и е разширение, предпочитано от Clang.
cl.exe използва разширението ixx. Би трябвало i да означава interface в този случай.
Относно GCC, аз лично не знам да има някакво разширение.

Компилация
За да компилираме модула, трябва да използвате възможно най-скоро излезлият компилатор на Clang или cl.exe. Мисля, че е възможно да работи и на GCC и да се използва, но тук няма да го обсъждаме, понеже повечето потребители са на windows. Ето и повечко информация за компилаторите ми:

clang:
Изображение

cl.exe:
Изображение

И конкретно тук започна забавата - чуденето на реда за компилация започна за clang++ и cl.exe

Код: Избери всички

clang++ -std=c++2a -fmodules-ts --precompile amxxbg.cppm -o amxxbg.pcm                    		// 1
clang++ -std=c++2a -fmodules-ts -c amxxbg.pcm -o amxxbg.o                                			// 2
clang++ -std=c++2a -fmodules-ts -fprebuilt-module-path=. amxxbg.o main.cpp -o amxxbg 		// 3

cl.exe /std:c++latest /experimental:module /TP /EHsc /MD /c amxxbg.cppm /module:interface /Fo: amxxbg.obj /module:output amxxbg.pcm 	// 1
cl.exe /std:c++latest /experimental:module /TP /EHsc /MD /c main.cpp /module:reference amxxbg.pcm /Fo: main.obj                 			// 2
cl.exe amxxbg.obj main.obj                                                                                                      									// 3
И сега какво реално се случва на всеки ред:
  1. Създава прекомпилиран модул amxxbg.pcm от декларацията на модула amxxbg.cppm
  2. Създава немодуларен машинен агрегатор
  3. Създава изпълним файл amxxbg или amxxbg.exe. За clang++ трябваше да задам пътя до модула.
По очевидни причини, няма да показвам изход от изпълнението на програмата. Бих го направил, ако имаше нещо сложно да показвам, че да не е ясно какво ще изпише.

Та, както виждате, от гледната точна на този, който имплементира, можем да разделим модула на модуларен интерфейсен агрегатор и модуларен имплементиращ агрегатор. Преди да премина към агрегаторите, нека се върна една крачка назад и да отговоря на въпроса.

Какви са предимствата на модулите?
  • Времето за компилация - Модулът се използва един път и би трябвало да е почти без никаква памет. Сравнете това с N на брой header-ни файлове, които включват M на брой машинни агрегатора. Комбинаторно, това означава, че имаме header-ен файл, който трябва да бъде обходен N * M пъти.
  • Изолация от препроцесорни макрота - Ако има един консенсус в цялото общество на C++, то тва ще да е да се премахнат тези препроцесорни макрота. Защо? Използването на макрота е просто замяна нна текст без никаква C++ семантика. Разбира се, вследствие на това има доста отрицателни последствия. Например, може да има значение в какъв ред са подредени макротата или макротата могат да си правят проблеми едно с друго с вече съществуващи такива в приложението ви. За разлика, за модулите няма никакво значение в какъв ред са подредени.
  • Логическа структура на кода - Модулите позволяват изричното експортиране на модули. Например, можете да си направите няколко подмодула в един по-голям модул и да кажете кои от тях да бъдат експортирани. По този начин, вие си правите логическа структура на кода и го предоставяте на потребителя като логически контейнер.
  • Отървавате се от грозните решения на даден проблем - Често има едни грозни подобия на макрота, които често казват "сложете си include guard около header-ният файл" или "напишете макротата с ГОЛЕМИ_И_ДЪЛГИ_ДУМИ". Това го правят, защото често има сблъсък между еднакви имена. За разлика, модулите нямат такъв проблем, идентичните имена няма да представляват проблем.
В първият си модул amxxbg, декларирах и дефинирах модула в един файл amxxbg.cppm. Нека поговорим за агрегати.

Модуларен интерфейсен агрегат и модуларен имплементиращ агрегат
Като за начало, новият модул amxxbg1 ще се съдържа от модуларен интерфейсен агрегат и модуларен имплементиращ агрегат.

Модуларен интерфейсен агрегат:

Код: Избери всички

// amxxbg1.cppm

export module amxxbg1;

export auto print() -> void;
  • Модуларният интерфейсен агрегат съдържа декларацията на модула, който искаме да експортираме: export module amxxbg1;
  • Имена като print могат да бъдат експортирани само в модуларният интерфейсен агрегат
  • Имена, които не са експортирани, могат да бъдат използвани само в модула
  • Един модул може да има само един модуларен интерфейсен агрегат
Модуларен имплементиращ агрегат:

Код: Избери всички

// amxxbg1.cpp

module amxxbg1;

auto print() -> void{
	std::cout << "amxx-bg.info" << std::endl;
}
  • Модуларният имплементиращ агрегат съдържа неекспортиращата декларация: module amxxbg1;
  • Един модул може да има повече от един модуларни имплементиращи агрегати
Основната програма:

Код: Избери всички

// main1.cpp

import amxxbg1;

auto main() {
	
	print();

	return 0;
}
  • От гледната точка на потребителя, името на модула се промени от amxxbg на amxxbg1.
Компилацията на модуларният модул е малко по-трудоемка.

Компилация на модула amxxbg1

Код: Избери всички

clang++ -std=c++2a -fmodules-ts --precompile amxxbg1.cppm -o amxxbg1.pcm               		// 1
clang++ -std=c++2a -fmodules-ts  -c amxxbg1.pcm -o amxxbg1.pcm.o                      			// 2
clang++ -std=c++2a -fmodules-ts -c amxxbg1.cpp -fmodule-file=amxxbg1.pcm -o amxxbg1.o    	// 2
clang++ -std=c++2a -fmodules-ts -c main1.cpp -fmodule-file=amxxbg1.pcm -o main1.o    		// 3
clang++  amxxbg1.pcm main1.o amxxbg1.o -o amxxbg                                         			// 4

cl.exe /std:c++latest /experimental:module /TP /EHsc /MD /c amxxbg1.cppm /module:interface /Fo: amxxbg1.pcm.obj /module:output amxxbg1.pcm  // 1
cl.exe /std:c++latest /experimental:module /TP /EHsc /MD /c amxxbg1.cpp /module:reference amxxbg1.pcm /Fo: amxxbg1.obj                      // 2
cl.exe /std:c++latest /experimental:module /TP /EHsc /MD /c main1.cpp /module:reference amxxbg1.pcm /Fo: main1.obj                     	       // 3
cl.exe amxxbg1.obj main1.obj amxxbg1.pcm.obj                                                  											       // 4
  1. Създаваме прекомпилираният модул amxxbg1.pcm от модулната декларация amxxbg.cppm
  2. Компилираме прекомпилираният модул amxxbg1.pcm и създава машинен агрегатор amxxbg1.pcm.o. Компилира source файла amxxbg1.cpp и създава машинен агрегатор amxxbg1.o. cl.exe прави това в една стъпка.
  3. Компилира основната програма main1.o или main1.obj
  4. Създава изпълним файл amxxbg1 или amxxbg1.exe

Какво следва?
Това е само въведение в модулите. В следващият пост ще навляза в повече подробности. Например, ще покажа изхода от програмата, ще говорим за стандартните header-ни файлове като <iostream> и за стандартните модули като std.core.

Отговори

Върни се в “Системно Програмиране”

Кой е на линия

Потребители, разглеждащи този форум: Няма регистрирани потребители и 1 гост