dlang-book/11-расширение-масштаба/README.md

16 KiB
Raw Blame History

11. Расширение масштаба

  • 11.1. Пакеты и модули
    • 11.1.1. Объявления import
    • 11.1.2. Базовые пути поиска модулей
    • 11.1.3. Поиск имен
    • 11.1.4. Объявления public import
    • 11.1.5. Объявления static import
    • 11.1.6. Избирательные включения
    • 11.1.7. Включения с переименованием
    • 11.1.8. Объявление модуля
    • 11.1.9. Резюме модулей
  • 11.2. Безопасность
    • 11.2.1. Определенное и неопределенное поведение
    • 11.3.2. Атрибуты @safe, @trusted и @system
  • 11.3. Конструкторы и деструкторы модулей
    • 11.3.1. Порядок выполнения в рамках модуля
    • 11.3.2. Порядок выполнения при участии нескольких модулей
  • 11.4. Документирующие комментарии
  • 11.5. Взаимодействие с C и C++
    • 11.5.1. Взаимодействие с классами C++
  • 11.6. Ключевое слово deprecated
  • 11.7. Объявления версий
  • 11.8. Отладочные объявления
  • 11.9. Стандартная библиотека D
  • 11.10. Встроенный ассемблер
    • 11.10.1. Архитектура x86
    • 11.10.2. Архитектура x86-64
    • 11.10.3. Разделение на версии
    • 11.10.4. Соглашения о вызовах
    • 11.10.5. Рациональность

Поговорка гласит, что программу в 100 строк можно заставить работать, даже если она нарушает все законы правильного кодирования. Эта поговорка расширяема: действительно, можно написать программу в 10 000 строк, уделяя внимание лишь деталям кода и не соблюдая никаких более масштабных правил надлежащей модульной разработки. Возможно, где-то есть и проекты в несколько миллионов строк, нарушающие немало правил крупномасштабной разработки.

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

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

В начало ⮍

11.1. Пакеты и модули

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

Нет причин думать, что исходному коду программы на самом деле будет удобнее в какой-нибудь супер-пупер базе данных. D использует «базу данных», которую долгое время настраивали лучшие из нас и которая прекрасно интегрируется со средствами обеспечения безопасности, системой управления версиями, защитой на уровне ОС, журналированием со всем, что бы вы ни назвали, а также устанавливает низкий барьер входа для широкомасштабной разработки, поскольку основные необходимые инструменты это редактор и компилятор.

Модуль D это текстовый файл с расширением .d или .di. Инструментарий D не учитывает расширения файлов при обработке, но по общему соглашению в файлах с расширением .d находится код реализации, а в файлах с расширением .di (от D interface интерфейс на D) код интерфейсов. Текст файла должен быть в одной из следующих кодировок: UTF-8, UTF-16, UTF-32. В соответствии с небольшим стандартизированным протоколом, известным как BOM (byte order mark метка порядка байтов), порядок следования байтов в файле (в случае UTF-16 или UTF-32) определяется несколькими первыми байтами файла. В табл. 11.1 показано, как компиляторы D идентифицируют кодировку файлов с исходным кодом (в соответствии со стандартом Юникод).

Таблица 11.1. Для различения файлов с исходным кодом на D используют ся метки порядка байтов. Шаблоны проверяются сверху вниз, первое же совпадение при сопоставлении устанавливает кодировку файла. xx любое ненулевое значение байта

Если первые байты... ...то кодировка файла ... Игнорировать эти байты?
00 00 FE FF UTF-32 с прямым порядком байтов1
FF FE 00 00 UTF-32 с обратным порядком байтов2
FE FF UTF-16 с прямым порядком байтов
FF FE UTF-16 с обратным порядком байтов
00 00 00 xx UTF-32 с прямым порядком байтов
xx 00 00 00 UTF-32 с обратным порядком байтов
00 xx UTF-16 с прямым порядком байтов
xx 00 UTF-16 с обратным порядком байтов
Что-то другое UTF-8

В некоторых файлах метка порядка байтов отсутствует, но у D есть средство, позволяющее автоматически недвусмысленно определить кодировку. Процедура автоопределения тонко использует тот факт, что любой правильно построенный модуль на D должен начинаться хотя бы с нескольких знаков, встречающихся в кодировке ASCII, то есть с кодовых точек Юникода со значением меньше 128. Ведь в соответствии с грамматикой D правильно построенный модуль должен начинаться или с ключевого слова языка D (состоящего из знаков Юникода с ASCII-кодами), или с ASCII-пробела, или с комментария, который начинается с ASCII-знака /, или с пары директив, начинающихся с #, которые также должны состоять из ASCII-знаков. Если выполнить проверку на соответствие шаблонам из табл. 11.1, перебирая эти шаблоны сверху вниз, первое же совпадение недвусмысленно укажет кодировку. Если кодировка определена ошибочно, вреда от этого все равно не будет файл, несомненно, и так ошибочен, поскольку начинается со знаков, которые не может содержать корректный код на D.

Если первые два знака (после метки порядка байтов, если она есть) это знаки #!, то эти знаки плюс следующие за ними знаки вплоть до первого символа новой строки \n игнорируются. Это позволяет использовать средство «shebang»3 тем системам, которые его поддерживают.

В начало ⮍ Наверх ⮍

11.1.1. Объявления import

Для получения доступа к благам стандартной библиотеки в примерах кода из предыдущих глав обычно использовалась инструкция import:

import std.stdio; // Получить доступ к writeln и всему остальному

Чтобы включить один модуль в другой, укажите имя модуля в объявлении import. Имя модуля должно содержать путь до него относительно каталога, где выполняется компиляция. Рассмотрим пример иерархии каталогов (рис. 11.1).

Предположим, компиляция выполняется в каталоге root. Чтобы получить доступ к определениям файла widget.d из любого другого файла, этот другой файл должен содержать объявление верхнего уровня:

import widget;

Рис. 11.1. Пример структуры каталога

«Объявление верхнего уровня» это объявление вне всех контекстов (таких как функция, класс и структура)4. Встретив это объявление import, компилятор начнет искать widget.di (сначала) или widget.d (потом) начиная с каталога root, найдет widget.d и импортирует его идентификаторы. Чтобы использовать файл, расположенный глубже в иерархии каталогов, другой файл проекта должен содержать объявление import с указанием относительного пути до него от каталога root с точкой . в качестве разделителя:

import acme.gadget;
import acme.goodies.io;

В объявлениях import мы обычно используем списки значений, разделенных запятыми. Два предыдущих объявления эквивалентны следующему:

import acme.gadget, acme.goodies.io;

Обратите внимание: файл, расположенный на более низком уровне иерархии каталогов, такой как gadget.d, также должен указывать путь к другим файлам относительно каталога root, где выполняется компиляция, а не относительно собственного расположения. Например, чтобы получить доступ к идентификаторам файла io.d, файл gadget.d должен содержать объявление:

import acme.goodies.io;

а не

import goodies.io;

Другой пример: если файл io.d хочет включить файл string.d, то он должен содержать объявление import acme.goodies.string, хотя оба этих файла находятся в одном каталоге. Разумеется, в данном случае предполагается, что компиляция выполняется в каталоге root. Если вы перешли в каталог acme и компилируете gadget.d там, он должен содержать объявление import goodies.io.

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

Объявление import присоединяет только идентификаторы (символы), следовательно, пакеты и модули на D должны иметь имена, являющиеся допустимыми идентификаторами языка D (см. раздел 2.1). Например, если у вас есть файл 5th_element.d, то вы просто не сможете включить его в другой модуль, поскольку «5th_element» не является допустимым идентификатором D. Точно так же, если вы храните файлы в каталоге input-output, то не сможете использовать этот каталог как пакет D. Иными словами, все файлы и каталоги, содержащие исходный код на языке D, должны носить лишь имена, являющиеся допустимыми идентификаторами. Дополнительное соглашение: имена всех пакетов и модулей не содержат заглавных букв. Цель этого соглашения предотвратить путаницу в операционных системах с нестрогой обработкой регистра букв в именах файлов.

В начало ⮍ Наверх ⮍


  1. Прямой порядок байтов от старшего к младшему байту. Прим. пер. ↩︎

  2. Обратный порядок байтов от младшего к старшему байту. Прим. пер. ↩︎

  3. «Shebang» от англ. sharp-bang или hash-bang, произношение символов #! Прим. науч. ред. ↩︎

  4. Текущие версии реализации позволяют включать модули на уровне классов и функций. Прим. науч. ред. ↩︎