http://www.aros.org AROS-Exec AROS-Exec Archives Power2People
kitty mascottop logo menu

Руководство по разработке приложений AROS

Index

Предупреждение

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

Содержание

Введение

Разработка программ для платформы AROS

В этой главе рассказывается о том, как разрабатывать программы, которые будут работать в AROS, а также Вы узнаете о том, как скомпилировать их на аппаратном обеспечении отличном от того, на котором работает AROS. Предполагается, что вы имеете хотя бы средние познания в языке C и таких основных понятиях программирования как компоновка.

Программа "Hello, world!"

Ниже показан пример программы, выводящей на экран сообщение "Hello, world!" - по программистской традиции дошедшей до нас из глубины веков. Файл helloworld.c содержит следующий программный код:

#include <stdio.h>

int main(void)
{
  puts("Hello World");
  return 0;
}

Компилирование в дереве исходников AROS при помощи системы сборки

Если вы имеете свою собственную копию дерева исходников AROS и компилируете AROS в ней, то вы можете воспользоваться системой сборки AROS для компилирования программ для AROS. Это делается путём размещения вашего программного кода где-нибудь в дереве AROS. Директория 'local' в основной директории исходников AROS может использоваться для размещения кода. Давайте сначала создадим в здесь поддиректорию, предполагая что сейчас вы находитесь в корне директории исходников AROS:

%mkdir local/helloworld

Поместите в неё файл helloworld.c и дополнительный файл с инструкциями по сборке по имени mmakefile.src, содержащий следующее:

include $(TOP)/config/make.cfg

%build_prog mmake=local-helloworld files=helloworld progname=HelloWorld

Теперь, находясь в корне директории исходников AROS, вы можете собрать программу HelloWorld при помощи следующей команды:

%make local-helloworld

Теперь вы найдёте в дереве бинарных файлов AROS скомпилированную программу local/helloworld/HelloWorld.

Система сборки AROS предназначена для упрощения вашей жизни при сборке бинарников с нетривиальными зависимостями. Об этом подробно рассказывается в отдельной главе.

Компилироание в Linux при помощи GCC

Под AROS/hosted вы используете сконфигурированную версию компилятора Linux GCC. Есть различия взависимости от того используете вы готовую скомпилированную версию AROS (i386-linux-system) или же компилируете исходники самостоятельно:

  • i386-linux-system

    Скачайте пакет i386-all-sdk. Распакуйте его, перейдите в созданную директорию и от имени суперпользователя root запустите находящийся там скрипт (например, sudo AROS-SDK-Install). Скрипт задаст несколько вопросов, вы можете согласиться со значениями, предлагаемыми по умолчанию. Далее надо добавить путь. Как это сделать зависит от командной оболочки, которой вы пользуетесь. Допустим вы используете Bash и настройки пути у вас остались по умолчанию. Тогда откройте файл /home/user/.bashrc и добавьте строку PATH=/usr/local/aros-sdk/bin:"${PATH}" в конец файла. Сохраните и перезапустите bash. Введите i386-aros-gcc -v для тестирования.

  • Самостоятельно скомпилированная

    Компилятор AROS имеет имеет путь AROS/bin/linux-i386/tools. Добавьте этот путь как показано выше. Имя компилятора i386-linux-aros-gcc.

Вы можете cкомпилировать программу при помощи следующей команды оболочки Linux:

% i386-linux-aros-gcc -o -heloworld helloworld.c

Вы найдёте дополнительные инструменты в директории компилятора AROS: AROS-версии программ ld, ranlib, компилятор каталогов Flexcat и т.д.

Примечание

Если вы используете i386-linux-aros-strip, то вам надо добавить параметры --strip-unneeded --remove-section .comment. Иначе strip-версия будет создавать повреждённые бинарники.

Компилирование для native-i386

Вы можете скачать версию GCC, которая работает непосредственно в AROS, с SourceForge . Вам нужны по крайней мере binutils и core. Также вам понадобится AROS SDK. Распакуйте его в то же место (например, sys:ADE). Скопируйте вложения и библиотеки из SDK в sys:ADE.

Затем используйте следующие команды:

path sys:ade/bin add
assign Development: sys:ade

Понятия

Файлы включений

AROS распространяется с разнообразными файлами включений (include). Они находятся в sys:Development/include. Поддиректория proto содержит файлы включений с прототипами функций для совместно используемых библиотек. В libraries располагаются файлы заголовков со структурами и определениями. Некоторые из больших библиотек, например, Intuition имеют свои собственные директории с заголовками.

Совместно используемые библиотеки AROS

Совместно используемые библиотеки являются той магией, благодаря которой AROS работает. Каждая библиотека представляет из себя набор функций, которые служат некоторой цели. Обычно функции со сходным назначением помещаются в одну библиотеку. Например, все основные функции управления памятью находятся в библиотеке exec.library.

Библиотеки обычно находятся в директории LIBS:, но могут храниться и в других местах. Некоторые особенно важные библиотеки хранятся не в отдельных файлах, а включены в ядро операционной системы. Помните, что такие библиотеки ядра могут различаться в разных версиях системы, поэтому не надо строить зависимостей от специфических библиотек, являющихся частью ядра.

Обзор системных библиотек AROS

Ниже приведён список некоторых важных библиотек и их функций. Нет нужды запоминать всё, что написано о них здесь, так как они подробно рассматриваются далее.

  • exec.library - наиболее важная библиотека. Она несёт ответственность за

    работу самых основных механизмов операционной системы: управления задачами (т.е. программами), памятью, библиотеками и многими другими важными вещами.

  • utility.library воплощает очень важные механизмы для обращения к

    библиотекам: taglist-ы, которые рассматриваются далее в этой главе и hook-и. Отдельно от этого, данная библиотека содержит разнообразные небольшие служебные функции.

  • dos.library отвечает за обращение с файлами, операции с дисками и некоторые

    основные функции ввода-вывода. Без этой библиотеки AROS не имела бы возможности работать с файлами.

  • intuition.library управляет графическими интерфейсами пользователя (GUI).

    При помощи этой библиотеки вы можете создавать окна и gadgets и, соответственно, управлять ими. Есть и другие библиотеки, которые работают поверх intuition и предоставляют более сложные и более специализированные функции для работы с графическим интерфейсом. Примером могут служить gadtools.library, которая реализует несколько более сложные gadgets, и asl.library, которая предоставляет функции формирования запросов к файлам и прочие формирователи запросов (requesters).

Как работают библиотеки AROS

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

В целом есть 2 типа библиотек: статические (link-time) и динамические (run-time). Статические библиотеки, как подсказывает название, используются только на этапе компоновки программы: компоновщик собирает все функции, которые нужны программе, из предоставленных библиотек и компонует их в исполняемый файл. Динамические библиотеки, вместо этого, доступны программам когда они запускаются или в процессе их выполнения посредством специальных запросов от программ. В большинстве систем динамические библиотеки используются выполняемыми программами совместно, вследствие чего в памяти загружается только одна копия библиотеки. В таких случаях их часто называют совместно используемыми библиотеками.

В то время как управление статическими библиотеками более или менее одинаково во всех операционных системах, т.к. они независимы от ОС сами по себе, управление динамическими библиотеками в разных операционных системах может осуществляться по-разному.

Перед тем как библиотека может быть использована в программе, она должна быть открыта. Это можно сделать выполнив функцию под названием OpenLibrary. После того как библиотека успешно открыта, возвращается указатель так называемой библиотечной базы. Библиотечная база - это область памяти, содержащая векторы функций и собственные данные библиотек [1]. Когда библиотеки открыты, они свободны выбирать будет их база одной и той же для всех копий или же каждый раз в памяти будет размещаться новая. Когда функция из библиотеки вызвана основное время библиотечная база передаёт функции следовательно данные напрямую могут быть использованы в библиотеке [2]. Библиотека может сделать часть данных или все данные целиком в библиотечной базе общедоступными по определению типа для базы. Если это имеет место, вы найдёте тип в файле включений proto/libname.h. Некоторые устаревшие библиотеки используют этот механизм, но более новые библиотеки не делают что-либо общедоступным и единственным способом изменить внутреннее состояние библиотеки является использование доступных функций.

[1]Если вы знакомы с C++, то можете думать о таблице векторов как о VTable, используемой для виртуальных методов, а об указателе библиотечной базы как об указателе this.
[2]Передача библиотечной базы может быть явной, либо неявной, в зависимости от соглашений используемых библиотекой. Также несколько механизмов могут быть неявно переданы базе: макросы препроцессора C, встраиваемые функции, глобальные переменные и т.д.

Как воспользоваться совместно используемыми библиотеками AROS

Как уже объяснялось в предыдущем разделе, библиотеки надо открывать до того как их функции можно будет использовать. Единственная библиотека, которую нет необходимости открывать, это exec.library. Exec является всегда открытой и ваш компилятор знает как получить к ней доступ. Дополнительно вам необходимо подключить заголовок, чтобы сделать прототип функций известным коду. Этот файл включений находится в директории proto, так что если вы хотите воспользоваться функциями, например, из библиотеки dos.library, то вам надо использовать следующую строку:

#include <proto/dos.h>

Ваш компилятор или среда сборки могут дополнительно открыть для вас некоторые библиотеки, следовательно, вам нет необходимоси открывать их вручную. Прочтите руководство к вашему компилятору, чтобы узнать о подобных особенностях. В следующих параграфах будет рассказано о том, как это сделать при помощи инструментов, имеющихся в AROS, и как открывать библиотеки вручную.

Автоматическое открытие при помощи gcc из AROS SDK

Компилятор GCC из набора AROS SDK автоматически открывает следующие основные библиотеки:

  • aros.library
  • asl.library
  • commodities.library
  • cybergraphics.library
  • datatypes.library
  • diskfont.library
  • dos.library
  • expansion.library
  • gadtools.library
  • graphics.library
  • icon.library
  • iffparse.library
  • intuition.library
  • keymap.library
  • layers.library
  • locale.library
  • muimaster.library (предоставляется в AROS проектом Zune)
  • partition.library
  • realtime.library
  • utility.library
  • workbench.library

Вы можете отключить автооткрытие этих библиотек, указав компилятору GCC флаг -nostdlibs. Для прочих библиотек предоставляемых AROS вы можете использовать соответствующие статические библиотеки, которые позаботятся от открытии библиотек. Следовательно, если, например, ваша программа использует библиотеку reqtools.library, то добавьте флаг -lreqtools в команду вызова компилятора GCC.

Примечание

Для обобщения: когда используете компилятор AROS GCC использование совместно используемых библиотек становится очень простым и может осуществляться в 2 шага:

  • Используйте оператор включения для объявления функций из библиотеки:

    #include <proto/reqtools.h>
    
  • Добавьте дополнительную ссылку на библиотеку, если библиотека автоматически не открывается GCC:

    % i386-linux-aros-gcc ... -lreqtools
    

Автоматическое открытие при помощи систмы сборки AROS

Автооткрытие библиотек системой сборки очень похоже на использование компилятора AROS GCC. Аналогично определению опции -l определите используемые вами библиотеки в параметре uselibs для %build_prog и макроса %build_module. Более подробную информацию вы можете найти в Руководстве по системе сборки.

Открытие библиотек вручную

Для открытия библиотеки вам надо использовать функцию из exec.library:

#include <proto/exec.h>

struct Library *OpenLibrary( STRPTR name, ULONG version );

Функции OpenLibrary() требуются 2 аргумента:

name

указывает имя библиотеки. Обычно это простое очевидное имя, но это может также быть полный (абсолютный или относительный) путь к библиотеке.

Примечание

Пути не работают с библиотеками, включёнными в ядро операционной системы. Используйте абсолютные пути только, если точно знаете, что делаете!

version
минимально допустимая версия открываемой библиотеки. Если названная библиотека найдена, но её версия ниже указанной, то библиотека не будет открыта, но будет возвращена ошибка. Версии важны поскольку допускается расширение библиотек. Некоторые функции доступны только начиная с определённых версий библиотеки. Например, функция AllocVec() из exec.library появилась в 36 версии этой библиотеки. Если вы попытаетесь вызвать эту функцию, имея установленной более старую версию exec.library, то случатся непредсказуемые вещи (скорее всего приложениеаварийно завершится).

Следующий порядок действий происходит при загрузке библиотеки для открытия:

  1. Во-первых, имя библиотеки ищется в списке уже загруженных библиотек. Если эта библиотека была загружена в память ранее (например, другой программой) и всё ещё находится там, то всё замечательно и значение функции OpenLibrary() будет немедленно возвращено.

    Библиотеки, включённые в ядро, всегда находятся в списке загруженных библиотек.

    Примечание

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

  2. Если библиотека не найдена в списке загруженных и вместе с name был передан путь, то будет произведена попытка открыть файл по указанному пути. Если это не получится, то функция OpenLibrary() вернёт ошибку.

  3. Если было передано только простое очевидное имя, то библиотеку будут искать сначала в текущей директории. Если там она не будет найдена, то далее её попробуют найти в директории LIBS:.

OpenLibrary() вернёт либо указатель на структуру, описывающую библиотеку (struct Library * определённую в exec/libraries.h), либо NULL, означающий, что по каким-то причинам открыть библиотеку неудалось. Итоговый указатель сохраняется для использования компилятором. Обычно он хранится в переменной в форме: <имя_библиотеки>Base, например, IntuitionBase для указателя на библиотеку intuition.library.

После открытия библиотеки, вы можете использовать, предоставляемые ею функции, просто вызывая их как любые другие функции в вашей программе. Но, чтобы позволить вашему компилятору узнать что делать, вам надо подключить относящийся к библиотеке заголовочный файл. Для компиляторов C это обычно делается строкой proto/<имя_быблиотеки>.h.

Когда вы закончили использовать библиотеку, вам надо закрыть её для освобождения используемых ею ресурсов. Это делается при помощи:

#include <proto/exec.h>

void CloseLibrary( struct Library *base );

Функция CloseLibrary() закрывает библиотеку указанную базой. Это также может быть NULL, в случае которого CloseLibrary() не вернёт ничего.

Мы продемонстрируем использование библиотек созданием маленькой графической программы приветствия. Вместо вывода на экран сообщения Hello World!, мы будем показывать его в диалоге запроса. Функцией, показывающей диалог запроса, является EasyRequestArgs() из библиотеки intuition.library. Мы не будем здесь обсуждать её использование. Более подробную информацию смотрите в разделе Requesters.

Пример использования библиотек:

#include <proto/exec.h>          /* OpenLibrary() и CloseLibrary() */
#include <exec/libraries.h>      /* struct Library */
#include <dos/dos.h>             /* RETURN_OK и RETURN_FAIL */
#include <proto/intuition.h>     /* EasyRequestArgs() */
#include <intuition/intuition.h> /* struct EasyStruct */

/* Эта переменная будет хранить указатель на intuition.library */
struct IntuitionBase *IntuitionBase;

int main(int argc, char *argv[])
{
    /* Нужно для EasyRequestArgs(). */
    struct EasyStruct es = {
      sizeof(struct EasyStruct), 0UL,
      "Requester", "Hello World!", "Ok"
    };

    /* Сначала мы открываем intuition.library. Нам нужна версия 36 или
       новее, потому что EasyRequestArgs() была введена именно начиная
       с этой версии intuition.library.
    */
    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36);

    /* Надо проверить, действительно ли intuition.library была успешно
       открыта. Если нет, то мы не должны вызывать функции из неё, поэтому
       мы немедленно вернём ошибку.
    */
    if (!IntuitionBase)
        return RETURN_FAIL;

    /* После открытия intuition.library, мы вызваем EasyRequestArgs(). */
    EasyRequestArgs(NULL, &es, NULL, NULL);

    /* И, наконец, нам надо закрыть intuition.library. */
    CloseLibrary((struct Library *)IntuitionBase);

    return RETURN_OK;
}

Попробуйте скомпилировать и запустить программу. Это представит вам красивую программу, показывающую диалог запроса "Hello, world!" с кнопкой Ok.

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

Совместно используемые библиотеки могут со временем развиваться и предоставлять новые возможности. Теперь, когда программа использует возможности более свежей версии библиотеки и её запускают в системе имеющей более старую версию, то наиболее вероятно, что это приведёт к аварийному завершению программы. Тем не менее нумерация версий библиотек введена для того, чтобы программы могли проверить, подходит ли версия библиотеки и элегантно завершиться или соответствующим образом ограничить свою функциональность, если нет. В AROS и Amiga-подобных операционных системах версия определяется старшим (major) и младшим (minor) номером (также им соответствуют "версия" и "ревизия"). Старший номер указывает на введение новых возможностей, в то время как увеличение младшего указывает лишь на некоторую оптимизацию и/или исправление ошибок, но с сохранением совместимости. Версию библиотеки часто указывают как major.minor [3], и её можно узнать при помощи команды version:

5.System:> version dos.library
dos.library 41.7

При открытии библиотеки вы можете указать номер версии и процесс открытия завершится неудачно, если версия библиотеки меньше, чем указанное значение:

mylibbase = OpenLibrary("my.library", 2);

Здесь будет возвращено значение NULL если установлена только версия 1 библиотеки my.library. Если вы используете автооткрытие библиотек, то будет открыта та версия библиотеки, которая использовалась во время компоновки. Эта версия может быть перезагружена при помощи переменной libbasename_version. В данный момент dos.library имеет версию 41 и это значит, что скомпилированные программы будут работать только в таких других системах, которые имеют 41-ю версию библиотеки dos.library. Если вы уверены, что используете возможности только версий до 36-й, то можете допустить вашу программу в эти системы со следующим оператором где-нибудь в вашем программном коде:

const LONG DOSBase_version = 36;

Как следствие, библиотеки всегда должны быть обратно совместимыми: если версия вашей библиотеки 41, а программа скомпилирована с версией 36, то она должна продолжать работать без проблем. Вследствие этого функция на определённом месте в таблице соответствия всегда должна выполнять точно те же действия даже для более новых версий библиотеки.

Если вы действительно хотите изменить поведение функции с определённым именем, вам надо сделать это, поместив её в другое место в таблице соответствия. Затем на старом месте расположите совместимую функцию, которая всё ещё будет совместима с поведением более старой версии библиотеки. Например, это было успешно проделано для функции OpenLibrary из exec.library. В первой версии AmigaOS она не имела такого параметра как версия и была размещена на месте 68. В более поздних версиях была введена функция OpenLibrary, имеющая парметр версии, и размещена на месте 92. Функция на позиции 68 была переименована в OldOpenLibrary.

[3]В противоположность тому, что некоторые люди думают, major.minor версия не является числовым значением: ревизией следующей после 1.9 будет 1.10, а 1.09 не является допустимым номером версии для AmigaOS в соответствии с принятой системой нумерации версий.

Отличия от других систем с динамическими библиотеками

Совместно используемые библиотеки AROS имеют уникальную архитектуру со своими преимуществами и недостатками. Некоторые аспекты будут рассмотрены далее в этой главе. В основном при этом мы будем ссылаться на Windows и UNIX(-подобные) системы.

Загрузка совместно используемых библиотек

В AROS динамически компонуемые библиотеки являются перемещаемыми ELF-объктами. В первое время после открытия библиотеки она загружается с диска и размещается по начальному адресу, в который она была загружена. В AROS и Amiga-подобных системах весь код, запущенный в системе использует одну большую область памяти. Это означает, что программы могут использовать библиотеки, также загруженные в память.

Другие системы, включая Windows и UNIX, имеют разные виртуальные адресные пространства для каждого процесса. В этом случае, операционная система также пытается загрузить совместно используемую библиотеку только однажды и затем пытается отобразить её в адресное пространство каждого процесса, использующего эту библиотеку. Таким образом, библиотека может располагаться по разным адресам в разных адресных пространствах и операционная система должна управлять этой ситуацией.

Windows пытается расположить совместно используемые библиотеки в определённом месте памяти и отобразить их в ту же область памяти, в которой находится каждый использующий её процесс. Если это невозможно, библиотека дублируется в памяти. В большинстве систем UNIX эта проблема обойдена путём разрешения компилятору генерировать позиционно-независимый код, например, код, который работает на любой позиции в памяти без необходимости его перемещения. В зависимости от архитектуры этот тип кода может иметь меньшее или большее влияние на скорость работы компилятора.

Динамическая компоновка функций

Програмисты, использующие высокоуровневые языки программирования для доступа к функциям совместно используемых библиотек, будут использовать то имя функции, которое захотят. Когда микропроцессор выполняет программу, то использует адреса памяти для перехода к определённой функции. Соответственно имя, используемое программистом, должно быть преобразовано в адрес в памяти.

В Amiga преобразование происходит, когда код компилируется или когда программа или модуль компонуются. Каждая libbase библиотеки AROS содержит таблицу соответствия для функций библиотеки. В процессе компиляции (или компоновки) имя функции преобразуется в позицию в этой таблице, в которой может быть найден адрес функции [4]. Таким образом, функции в совместно используемых библиотеках AROS доступны с одним уровнем преобразования. В зависимости от архитектуры ЦПУ этот уровень преобразования может иметь большее или меньшее влияние на скорость работы кода. К счастью, подобный тип преобразования используется для вызова виртуальных функций в классах C++ и наиболее современные ЦПУ оптимизированы для управления преобразованием без (большого) влияния на скорость. Так как таблица соответствия прикреплена к libbase, то её надо дублировать для библиотек, использующих per-opener base.

В Windows и UNIX-подобных системах преобразование имени функции в адрес происходит когда программа загружается в память и динамически компонуется с совместно используемыми библиотеками [5]. Когда программа компонуется во время компилирования, список библиотек помещается в исполняемый файл вместе со списком используемых функций. Эти списки являются ASCII строками. Когда программа впоследствии загружается, имена функций конвертируются в их адреса (или в указатель в таблице соответствия). Сначала библиотеки из списка библиотек открываются, после чего каждая функция ищется в библиотеках. Для поиска имён функций используются различные механизмы. Например, в Windows доступные функции помещаются в отсортированный массив, так что может быть произведён двоичный поиск, а в Linux для ускорения поиска используются хэши.

Глобальные и статические переменные в библиотеках

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

int globvar;

Глобальная переменная будет доступна из всех частей библиотеки. Когда совместно используемая библиотека загружена в память, ваша переменная также будет размещена в памяти, занятой библиотекой и будет всегда оставаться на одном и том же месте, пока библиотека не будет выгружена из памяти. Статические переменные, определённые в функции, управляются аналогичным способом. Это также подразумевает, что код в библиотеке обращающийся к глобальной переменной будет всегда загружаться в одно и тоже место в памяти, неважно сколько раз библиотека будет открываться или какая программа вызовет код из библиотеки. В настоящее время единственным способом получить переменную, которая имеет различное значение для каждого открывающего библиотеку, является библиотека с per-opener base и хранение библиотеки в этой базе. Также в данный момент глобальные переменные не экспортируются из совместно используемых библиотек AROS. Они могут быть доступны только внутри самой библиотеки, программа, использующая библиотеку, не имеет прямого доступа к глобальным переменным библиотеки. В этом отношении переменные в совместно используемых библиотеках AROS управляются отличным образом от переменных в статических библиотеках. Глобальная переменная определённая в статической библиотеке тоже доступна программе, с которой библиотека скомпонована, и каждая программа, скомпонованная с этой же статической библиотекой, будет иметь свою собственную версию глобальной переменной.

В UNIX, совместно используемые библиотеки были введены после того, как статические библиотеки уже интенсивного использовались. Одним из преимуществ дизайна стало то, что поведение совместно используемых библиотек было таким же, как и поведение статических. Поэтому каждый раз, когда программа открывала совместно используемую библиотеку, делалась копия переменных. В этом случае каждая программа, открывшая совместно используемую библиотеку, получала свой собственнй набор глобальных переменных. Также глобальные переменные совместно используемой библиотеки автоматически экспортировались из этой библиотеки, вследствие чего были доступны напрямую из программы, использующей эту библиотеку.

В Windows можно выбрать поведение глобальных переменных как в AROS, либо как в UNIX, но по умолчанию они управляются как в UNIX.

Для портирования совместно используемых библиотек в AROS или Amiga эти отличия в упрвлении перменными должны учитываться. Некоторые библиотеки зависят от того, как переменные управляются в совместно используемых библиотеках UNIX и Windows, и их портирование в AROS может оказаться затруднительным.

Примечание

В этом параграфе объясняется как работает управление данными в совместно используемых библиотеках, когда текст был написан. В данный момент также обсуждалось то, как распространить похожий метод управления на управление другими типами библиотек.

Библиотеки, использующие другие библиотеки

Библиотека может открыть другую библиотеку. После того, как библиотека открыла другую библиотеку, она получает базу этой библиотеки. Это означает, что библиотека, имеющая базу per-opener , вернёт другой библиотеке уникальный указатель базы. Когда программа открывает библиотеку с per-opener base, обратно она получит базу. Теперь, когда программа открывает вторую библиотеку, которая открывает первую библиотеку, другая база будет возвращена второй библиотеке, и затем - программе. Те, кто создаёт библиотеки с per-opener libbase должны это учитывать.

Как уже говорилось выше, в UNIX и Windows всё основано на процессах. Когда программа загружается, создаётся новый процесс, каждая совместно используемая библиотека используемая этим процессом будет только однажды динамически скомпонована с процессом. Это значит, что программа и совместно используемая библиотека, имеющие доступ к другой совместно используемой библиотеке, будут использовать одну и туже копию этой другой библиотеки. Это различие в поведении может сделать портирование совместно используемых библиотек из UNIX/Windows затруднительным.

Примечание

В этом параграфе объясняется как работает управление открытием совместно используемых библиотек из библиотек, когда текст был написан. В данный момент также обсуждалось то, как распространить похожий метод управления на управление другими типами библиотек.

[4]На самом деле, в разных видах AROS, эта таблица может содержать больше, чем просто указатели функций. В AROS для платформы 68k, фактически, где двоичная совместимость с AmigaOS (tm) является проблемой, каждое включение в таблице содержит инструкцию JMP, следующую за адресом функции (который таким образом является частью кода операции JMP), и программа пользователя не перехдит к адресу в векторе, они переходят к самому вектору, и затем инструкция JMP перенаправляет поток данных программы правильной функции.
[5]Поправить: совместно используемая библиотека a.out, cardinal в Windows и т.д.

Подача дополнительных аргументов через taglist

Каждая функция библиотеки требует фиксированное число аргументов. Это ставит проблему со сложными функциями, которые могут нуждаться в большом количестве аргументов. Для обхода этой проблемы были введены так называемые списки тегов (taglist). В utility/tagitem.h мы находим структуру TagItem, которая включает в себя члены ti_Tag и ti_Data. Список тегов содержит массив таких структур. Размер списка неограничен. Поле ti_Tag является идентификатором (часто упоминается как Tag), который декларирует содержимое ti_Data. ti_Data является либо целочисленным типом, либо указателем. Это гарантирует наименьший размер long-word или указателя (какой бы из них не был больше).

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

Есть несколько специальных тегов, которые понимают все функции (это определено в utility/tagitem.h):

TAG_DONE и TAG_END
Определяет конец списка тегов. Каждый список тегов должен оканчиваться одним из них. Последующий ti_Data должен быть проигнорирован вызываемой функцией, поэтому нет необходимости нахождения в памяти.
TAG_IGNORE
означает что содержимое ti_Data игнорируется. Этот тег особенно полезен для условного включения тегов.
TAG_MORE
Используя этот тег, вы можете связать списки тегов воедино. ti_Data указывает на другой список тегов. Обработка текущего списка тегов будет остановлена и вместо неё будет обрабатываться новый список тегов. Этот тег также завершает текущий список тегов.
TAG_SKIP
принуждает парсер пропустить следующие теги ti_Data. Они не будут обработаны.

Вы также можете передать списку тегов NULL вместо указателя. Все функции должны быть способны управлять указателями типа NULL. Они равнозначны спискам тегов, в которых первым тегом является TAG_DONE.

Функция, которая нуждается в списке тегов:

#include <proto/intuition.h>

struct Window *OpenWindowTagList
(
    struct NewWindow *newwin, struct TagList *taglist
);

Теперь вам надо только знать, что эта функция открывает новое окно. Мы зададим аргументу newwin значение NULL. Единственные теги, используемые сейчас:

Тег Описание Тип
WA_Width Ширина окна в пикслях UWORD
WA_Height Высота окна в пикслях UWORD
WA_Title Заголовок окна STRPTR

Другая функция, необходимая нам в этом небольшом примере:

#include <proto/intuition.h>

void CloseWindow( struct Window *window );

Эта функция используется для закрытия открытого окна.

Теперь давайте посмотрим на другую небольшую программу hello-world. Она открывает окно, в заголовке которого написано "Hello, world!", за 2 секунды:

#include <proto/exec.h>
#include <exec/libraries.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <intuition/intuition.h>

struct DosLibrary    *DOSBase;
struct IntuitionBase *IntuitionBase;

int main(int argc, char *argv[])
{
    int error = RETURN_OK;

    /* Это нам понадобится позднее для функции Delay(). */
    DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36);
    if (DOSBase)
    {
        IntuitionBase = (struct IntuitionBase
        *)OpenLibrary("intuition.library", 36);
        if (IntuitionBase)
        {
            struct Window *win;
            /* Задаём значения нашим тегам */
            struct TagItem tags[] =
            {
                { WA_Width, 100                  },
                { WA_Height, 50                  },
                { WA_Title, (IPTR)"Hello World!" },
                { TAG_DONE, 0UL                  }
            };

            win = OpenWindowTagList(NULL, tags);
            if (win)
            {
                /* Теперь ждём 2 секунды, после чего мы увидим наше окно.
                */
                Delay(100);

                /* Закрываем наше окно. */
                CloseWindow(win);
            }

            CloseLibrary((struct Library *)IntuitionBase);
        }
        else
            error = RETURN_FAIL;

        CloseLibrary((struct Library *)DOSBase);
    } else
        error = RETURN_FAIL;

    return error;
}

Конечно, этот метод задания списка тегов достаточно замысловатый. Поэтому для большинства функций, использующих списки тегов, доступны сокращённые варианты. Статическая библиотека amiga.lib предоставляет эти сокращённые варианты всем внутренним функциям AROS. Эти varargs версии можно использовать следующим образом:

#include <proto/alib.h>

Function( arg1, ..., argn, TAG1, data1, ..., TAG_DONE );

Наш пример выше будет выглядеть так, используя varargs версию функции OpenWindowTagList(), называющуюся OpenWindowTags():

[...]

if( IntuitionBase )
{
    struct Window *win;

    win = OpenWindowTags
    (
        NULL, WA_Width, 100, WA_Height, 20,
        WA_Title, "Hello World!", TAG_DONE
    );
    )
    if( win )
    {

[...]

Гораздо проще, не так ли?

Получение большего количества документации

"Hello, world!" - не вершина таланта программиста, и, возможно, вам будет любопытно, есть ли в AROS что-то поинтереснее. Ещё бы, дорогие, конечно есть. Но это руководство не является ни Руководством программиста, ни Справочным руководством программиста. Такое руководство может быть будет написано в будущем, а пока лучшими Руководствами программиста под AROS, которые вы можете найти, являются книги, написанные для Amiga, а лучшим справочником по AROS - autodocs (autodocs описывают функции библиотек AROS, которые создаются путем синтаксического анализа исходного кода AROS).

В основном, они полезны опытным программистам для Amiga: они содержат только очень краткое объяснение каждой функции. Если вы хотите научиться программированию под AROS с самого начала, вам действительно следует попытаться найти старую книгу об Amiga или купить компакт-диск разработчика для Amiga (Amiga Developer CDRom, RKM - ROM Kernel Manuals).


Copyright © 1995-2024, The AROS Development Team. Все права защищены.
Amiga© является торговым знаком Amiga Inc. Все прочие торговые знаки принадлежат их собственникам.