[Autumn Series] Повече информация за модулите

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

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

[Autumn Series] Повече информация за модулите

Мнение от Autumn Shade EA\DICE » 13 май 2019, 10:54

Съдържание:
  • Припомняне
  • Стандартни модули
  • Модулите във Visual Studio 2019
  • Експортирани и неекспортирани модули
  • Повече информация
  • Какво следва?

В предишната си тема ви дадох малко информация за това какво представляват модулите в C++20. В тази тема ще видим как ще ги използваме. Преди да започна темата, нека припомним докъде стигнахме.

Припомняне
Създадох модул с името amxxbg, който имаше модуларен интерфейсен агрегат и модуларен имплементиращ агрегат, както и main.cpp, който се води потребител и който използваше тези два агрегата. Тук са трите сорс файла:

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

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

// amxxbg1.cppm

export module amxxbg1;

export auto print() -> void;
Модуларен имплементиращ агрегат:

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

// amxxbg1.cpp

module amxxbg1;

auto print() -> void{
	std::cout << "amxx-bg.info" << std::endl;
}
Основната програма:

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

// main1.cpp

import amxxbg1;

auto main() {
	
	print();

	return 0;
}
Компилирах програмата с текущата версия на clang и cl.exe компилатора. От сега нататък, ще се придържаме към cl.exe компилатора, защото редовете са малко по-къси. Както и обещах, от предният пост ще пусна програмата и ще покажа какво излиза.

Стандартни модули
Съществено, нито модуларняит интерфейсен агрегат, нито модуларняит имплементиращ агрегат се променят в модула amxxbg1.

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

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

// amxxbg1.cppm

export module amxxbg1;

export auto print() -> void;
Модуларен имплементиращ агрегат:

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

// amxxbg1.cpp

module amxxbg1;

auto print() -> void{
	std::cout << "amxx-bg.info" << std::endl;
}
Основната програма:

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

// main1.cpp

import std.core;

import amxxbg1;

auto main() {
	
	print();

	return 0;
}
Благодарение на модула std.core, мога да покажа резултатът от изпълнението на програмата.
Изображение

Използването на хедърният файл iostreamе възможно, разбира се, усещам и накъде отиват работите - кои модули са достъпни? Ето какво видях от поста на Microsoft "Using C++ Modules in Visual Studio 2017" в team блога им.
  • std.regex - Съдържа всичко от хедърният файл <regex>
  • std.filesystem - Съдържа всичко от хедърният файл <experimental/filesystem>
  • std.memory - Съдържа всичко от хедърният файл <memory>
  • std.regex - Съдържа всичко от хедърният файл <regex>
  • std.threading - Съдържа всичко от хедърните файлове <atomic>, <condition_variable>, <future>, <mutex>, <shared_mutex>, <thread>
  • std.core - Съдържа всичко от останалата част на стандартната библиотека
Модулите позволяват повече абстракция от хедърите. Това ги прави доста удобни за използване и като допълнение, можете да укажете кой модул да бъде експортиран и кой не.

Експортирани и неекспортирани модули
Нека към сегашният ни модул да добавим малко повечко методи и да видим къде идва разликата.

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

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

// amxxbg1.cppm

export module amxxbg1;

auto addition(int, int) -> int;
export auto multiplication(int, int) -> int;

export auto print() -> void;


Модуларният интерфейсен агрегат съдържа декларациите на експортираният модул: export module amxxbg1; Модулната декларация започва с така наречената модулна компетентност/сфера/област. Само имена след модулната компетентност/сфера/област, които са декларирани с ключовата дума export са експортирани. Ако не са, името не е видимо извън модула и следователно има връзка с модула. Това, в този пример, е конкретно за addition, но не важи за функциите multiplication и print.

Модуларен имплементиращ агрегат:

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

// amxxbg1.cpp

import std.core;

module amxxbg1;

auto addition(int a, int b) -> int {
	return a + b;
}

auto multiplication(int a, int b) -> int {
	return a * b;
}

auto print() -> void {
	printf("amxx-bg.info");
}
Няма какво много да се добави за модуларният имплементиращ агрегат. По-интересното се случва в основната програма.

Основната програма:

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

// main1.cpp

// #include <iostream>    // (1)
// #include <numeric>     // (1)
// #include <string>      // (1)
// #include <vector>      // (1)
import std.core;          // (2)

import amxxbg1;

auto main() -> int {

    std::cout << std::endl;

    // std::cout << "Addition of 1000 and 15 = " << addition(1000, 15) << std::endl;

    std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7 , 8, 9, 10 };

    auto result = std::accumulate(numbers.begin(), numbers.end(), 1, multiplication);

    std::cout << "std::accumulate(numbers.begin(), numbers.end(), multiplication) = " << result << std::endl;
	
	print();

	return 0;
}
Виждате, модулите са доста удобни, в този случай, вместо да използваме четири хедърни файлове на редовете (1), ние просто използваме един ред import std.core на ред (2). Това е. Ето примерен изход от програмата:

Изображение

Сега въпросът е, какво ще стане ако махнем коментара на този ред, където е извеждането и използването на функцията addition? Да си припомним, че addition не беше експортирана функция, следователно, ще има връзка само с модула.

Изображение

Компилаторът се оплаква, че функцията addition е използвана в main програмата, без да е декларирана или по-конкретно, видима.

Повече информация
Първо, можете да експортирате по няколко начина:
  • Всеки метод поотделно
  • По групи
  • Цял namespace
Всеки метод поотделно

Ето пример как всеки метод поотделно се експортира. Това използвахме и досега.

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

// amxxbg1.cppm

export module amxxbg1;

auto addition(int, int) -> int;
export auto multiplication(int, int) -> int;

export auto print() -> void;

По групи

Освен всеки метод поотделно да го експортирате и да пишете някакви ключови думи всеки път на всеки метод, можете да направите и така:

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

// amxxbg1.cppm

export module amxxbg1;

auto addition(int, int) -> int;

export {
	auto multiplication(int, int) -> int;

	auto print() -> void;
}

Цял namespace

Третият вариант, и на мен който всякаш ми е най-удобен, цял namespace да бъде експортиран. Позволява максимално ниво на модуларност и разделение на кода в същото време, подреденост.

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

// amxxbg1.cppm

export module amxxbg1;

namespace amxxbg1 {
	auto addition(int, int) -> int;
}
export namespace amxxbg1 {
	auto multiplication(int, int) -> int;

	auto print() -> void;
}
Всичките три варианта по семантика нямат разлика. Може също да се чувствате по-добре ако ре-експортирате модули/методи/операции.

Ре-експортиране на модул
Понякога, искате да експортирате нещо, което сте импортирали от друг модул. Ако нямате експорт на импортираният модул, импортираният модул има последователно свързване с модула и имената му не са видими извън самият него. Ето един конкретен пример:

Видими срещу невидими
Представете си, че искам да импортирам и използвам модула amxxbg.core и amxxbg.core_new в нов модул amxxbg. Ето го модулните интерфейсни агрегати на amxxbg.core и amxxbg.core_new:

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

// модуларен интерфейсен агрегат amxxbg.core

export amxxbg.core;

export auto multiplication(int, int) -> int; 

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

// модуларен интерфейсен агрегат amxxbg.core_new

export amxxbg.core_new

export auto addition(int, int) -> int; 
След това, искам да направя нов модул amxxbg:

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

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

export module amxxbg;

import amxxbg.core;           // не се ескпортира с multiplication
export import amxxbg.core_new;   // експортира се с addition

multiplication(1000, 2);	// всичко точно
addition(1000, 100);		// тук също
Както виждаме, всичко е точно като използваме експортираните и неекспортираните имена в модула amxxbg, но модула amxxbg.core не е експортиран. Само потребителят, който използва модула amxxbg ще забележи разликата.

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

// Модуларен имплементиращ агрегат на amxxbg

import amxxbg;

multiplication(1000, 2);	// Грешка
addition(1000, 100);		// тук също
Функцията multiplication има връзка с модула си, следователно, не е видима извън модула. Само функцията addition е видима.

Препакетирани модули
Има и по-удобен начин за представянето на подобни модули, както и да се избегне тази грешка отгоре. С пълна сила важат всички правила и начини за експортиране споменати по-горе. Ето един пример с групи:

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

export module amxxbg;

export{

    import amxxbg.core;
    import amxxbg.core_new;
    import amxxbg.basics;
    
}
 
Това прави всички имена видими за потребителя, който импортира модула amxxbg.

Какво следва?
За сега мисля да се поизчакат някои неща да се изчистят сред C++20 и ще се връщам назад към C++17, C++14 и C++11.

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

Bartian
Потребител
Потребител
Мнения: 161
Регистриран: 01 сеп 2018, 14:43
Се отблагодари: 17 пъти
Получена благодарност: 50 пъти

[Autumn Series] Повече информация за модулите

Мнение от Bartian » 13 май 2019, 17:02

Каква е разликата между cpp и cppm разширенията. При положение че срр се използва в предпочитание от Clang
Еби, ебавай, преебавай но заради ПУТКА, ПУТКА не ставай!!!
Знаеш ли кое е лошото на предателството, че НИКОГА, НИКОГА не идва от непознат!!!

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

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

[Autumn Series] Повече информация за модулите

Мнение от Autumn Shade EA\DICE » 13 май 2019, 18:14

Bartian написа:
13 май 2019, 17:02
Каква е разликата между cpp и cppm разширенията. При положение че срр се използва в предпочитание от Clang
Няма такава. CPPM - C Plus Plus Module. Прави се с цел по-лесно разбиране на йерархията на даден проект. Като отвориш папката да знаеш кое къде да го търсиш.

Отговори

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

Кой е на линия

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