Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Обучение от Mail.Ru Group.
Онлайн-университет
для программистов с
гарантией трудоустройства.
Набор открыт!

2004 г.

Особенности программирования BeOS API для пришельцев - введение. Часть 3-я.

Сергей Долгов, www.qube.ru

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


Замечание мимоходом - снова об инструментах разработки

Во-первых, если у кого вдруг на системе еще нет ни BeIDE, ни BeBook ни компилятора С++ - все это можно сгрузить с bebits.com, из раздела "BeOS Personal Edition", пакет называется BeOS R5 Development Tools(17.9 MB). Если сей линк не сработал, посмотрите внимательней, там довольно большой список зеркал. Архив надо распаковать в /boot/

Теперь про BeIDE. Это не компилятор, а интергрированная среда разработки, способная работать практически с любым компилятором (например с Java и Pascal). По умолчанию она заточена на компилятор gcc (всякие другие имена компиляторов, типа cc или g++, тоже работают, но представляют собой или линки или скрипты, исользующие gcc). Упоминавшийся в одном из комментариев компилятор от Metrowerk (mwcc) ныне присутствует только в версии для PowerPC, но не для x86.

Библиотеки располагаются в двух местах:

/boot/beos/system/lib/ (основные системные)

и

/boot/home/config/lib/ (дополнительные)

а в /boot/develop/lib/x86/

располагаются в основном линки на библиотеки из указанных выше мест.

Заголовочные файлы располагаются в

/boot/develop/headers/

и

/boot/home/config/include/

Впрочем, эти факты можно было бы узнать, набрав в терминале незамысловатую команду:

set
или 
set | more
которая, среди прочего, выдаст вам значения
таких переменных, как: BEINCLUDES BELIBRARIES BE_CPLUS_COMPILER BE_C_COMPILER CPLUS_INCLUDE_PATH C_INCLUDE_PATH LIBRARY_PATH

впрочем, на начальном этапе, возможно, не все из них будут присутствовать в вашем списке. BeBook после правильной распаковки пакета разработчика будет располагаться по адресу "/boot/beos/documentation/Be Book/index.html".

Считаем, что эту вашу проблему мы решили, теперь упомяну о моей - нынешний движок qube.ru не слишком приспособлен для ввода-вывода хорошо форматированного кода в статьях, так что с красивыми отступами у нас могут быть проблемы, так же, как может быть, и с прямым копирование кода из браузера в BeIDE.


Итак, в предыдущей части введения мы, наконец, успели сказать, что настоящая BeOS-программа должна содержать объект класса BApplication, а запускаться все это должно вызовом метода Run() класса BApplication.

Ну что ж, самое время для небольшого обещанного базара о классах и всем таком прочем. Знатоки ООП эту часть смело могут пропустить, хотя бы для того чтобы уберечь челюсть от повреждений в результате ухмылок. Единственно, что стоит для них упомянуть, так же как и для новичков - никакой другой возможности (эффективно) программировать в BeOS, кроме C++ OOП API на данный момент нету. Поскольку просто не существует другого API, кроме полноценного объектного под C++. Есть правда компилятор Basic, который транслирует Basic-код в C++, а последний потом компилируется нормальным образом, но такому способу недостает ни полноты, ни гибкости. Так же не хватает полноты и гибкости и для существующей версии Pascal (с до сих пор не законченной библиотекой оберток вокруг нативных вызовов Be C++ API).

Даже на С нельзя писать почти ничего, что выходило бы за рамки консольных приложений и драйверов.

Так что рекомендация писать на C++ в BeOS - это не дурная шутка, вроде MFC, и не удачный прикол, вроде OWL и VCL - а это то, из чего BeOS сама сделана, снизу доверху, за исключением (частей)ядра и (частей) драйверов.

Впрочем, те, кто не специалист в ООП, тоже могут пропустить это сжатое и слегка сумбурное изложение, а вместо этого в спокойной обстановке просмотреть Обзор Объектно-Ориентированного Программирования..

Классы и объекты, отступление первое

    Хаотичное программирование a-la GOTO/BASIC и даже классическое структурное-процедурное программирование сильно напоминают один анекдот, про технологическую карту на советском авиазаводе, где пораженному новичку в сборочном цехе неожиданно предстало нечто, гораздо более напоминающее паравоз, а не истребитель.

    В ответ ветеран производства ткнул его носом в последнюю инструкцию на карте: "До окончательного вида изделие довести рашпилем".

    Объектное же программирование больше напоминает ситуацию в западном автомобильно производстве, когда многочисленные модели Seat, Skoda, VW - всего лишь вариации на тему относительно небольшой линейки шасси и прочих компонент.

    Что же такое класс? Это упакованная в одно целое коллекция процедур, функций и (структур) данных. Все эти функции по большей части предназначены для работы с данными, упрятанными внутри этого самого класса. В некотором смысле класс - это развитие понятия структур из языка C. Но просто собрать это все вместе каким-то образом - невеликое достижение, и толку для программера от этого было бы чуть. Примерно, как от способности копипэйстить свой и чужой код из старых программ:)

    Главное качество классов-объектов для программиста-"лентяя" - возможность делать из существующих объектов, НЕ ТРОГАЯ (и часто вообще не имея доступа к) код этого объекта, свой собственный прекрасных объект всего лишь парой легких мазков.

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

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

    К примеру, в заголовочном файле KrutojeOkno.h у нас будет строчка c таким куском:

    class KrutojeOkno : public BWindow

    Таким элементарным способом мы создаем (объявляем) совершенно новый класс (как это происходит - забота компилятора, а не наша:), но который, при этом, независимо от того, как мы его обвесим причиндалами в дальнейшем, будет обладать автоматически всеми свойствами нормального BeOS-окошка (класс BWindow).

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

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

    Естественно, что наследуются и всякие полезные фунции-методы класса-родителя (для этого в языке С++ требуется, чтобы в объявлении было слово public перед названием родительского класса).

    В нашем случае, это, например, Zoom(), Minimize(), SetTitle().

    То есть, чтобы в программе в дальнейшем изменить заголовок окна, вам придется написать нечто вроде:

    KrutojeOkno->SetTitle("Eto mojo pervoje okno!");

    Те, кто пытался на ассемблере или даже в BASIC для DOS написать программульку, рисующую рамку, в которой точка бегала бы за курсором, должны представлять себе, что это самое окно, которое BWindow - не фунт изюма. Кода всяческого там внутри тьма - но нам-то какое до этого дело?

    Например до того, где и как хранится название/титул окна - в char title[], или в AnsiString WindowName. Все знать - это не знать ничего, поскольку невозможно.

    Для наших целей достоточно уметь установить название (KrutojeOkno->SetTitle()) или его узнать (nazvanije = KrutojeOkno->Title()). То же самое с механизмом изменения размера окна - что там внутри и как происходит, как называются переменные, хранящие эти размеры, какие при этом сообщения бегают между окном и сервером приложений - не наше дело. Наше дело - уметь установить нужный нам размер окна - KrutojeOkno->ResizeTo(ширина, высота), или узнать нынешние размеры -

    ramka = KrutojeOkno->Frame();
    

    Такое вот отсекание лишних для прикладного программиста сущностей методом упрятывания с глаз долой и называется инкапсуляцией (encapsualtion).

    Идея термина понятна - все, что не должно путаться под ногами-руками, как бы засовывается в непрозрачную капсулу:)

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

    Многие моджахеды от ООП вообще считают, например, что никакие переменные класса не могут быть доступны извне "напрямую". Например, если у нас есть класс точки Point (состоящий из X и Y), то за такое его воплощение, где можно написать Point.X = 0, моджахеды эти предлагают отрезать половину седалища и убивать на месте методом скармливания тонны овсянки без соли. По их мнению, право на жизнь имеет только такая конструкция Rect.SetX(0). Впрочем, OOП-языки - это такая мощная штука, которая позволяет удовлетворить и правых и левых, например, когда вы пишете Point.X = 0 - на самом то деле может вызываться целая куча методов, которые, например, проверяют, допустимый ли это X и когда вы последний раз чистили зубы.

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

    class KrutojeOkno : public BWindow
    {
    BButton* GlavnajaKrasnajaKnopka;
    }
    

    Таким вот простым способом мы утерли нос ребятам из Be Inc - в нашем окошке кроме глупых рамки и желтой полоски будет существовать еще и отличная Главная Кнопка.

    Ясно конечно, что все не совсем так просто - эту кнопку надо как-то инициализировать и пришпандорить к окну, а может даже заставить что-то делать - но это уже другая история. Не очень страшная, впрочем.

    Да, тут подошло время традиционного объяснения различия между словами класс и объект (класса). За дальнейшее объяснение экстремисты и пуристы могут меня и замочить, но я смелый, когда надо, чтоб народу было понятнее. Класс - это заготовка, шаблон, мертвая последовательность байт в библиотеке. Объектом класс становится, когда наполняется жизнью - то есть когда запускается программа, выделяется место в памяти и происходит инициализация этого ранее мертвого кода и лежачих данных. Опять же по традиции, во всяческих пособиях для уменьшения словофлудия заместо обоих слов обычно говорят просто - класс! А там уж из текста обычно понятно, о чем речь.

    Еще один столп, на котором прочно устроилось ООП - это полиморфизм (polymorphism).

    (Попутно будет к месту упомянуть и т.н. перегрузку (overloading)

    Чуть выше, в разговоре про экстремистов, мы отметили, что невинный значок присваивания может оказаться совсем непростой вещью. Что понятно.

    Например, если мы пишем Ekran1 = Ekran2, где оба члена выражения происходят от класса BBitmap (по сути - трехмерный массив байтов) - оператор "=" делает за сценой совсем не то же, когда мы присваиваем одно целое другому (int x, y; x = y;)

    ООП-языки позволяют вам создавать свои определения давно закомых значков операторов, всяких "+", "-", "=", подобный трюк и называется overloading, но рассказ об этом явно лежит за рамками "Курса начинающего пчеловода".)

    Так вот, полиморфизм - это когда за одним и тем же именем функции (как и за операторами в предыдущем примере) могут скрываться совершенно разные вещи. Ну вот грубоватый пример - рисование линии - под одним и тем же названием Linija мы скроем две разные по действию функции:

    int Linija(int x1, int y1); - чертит линию
    от текущего положения (пера) до точки x1,y1. int Linija(int x0, int y0, int x1, int y1);
    - чертит линию от x0,y0 до x1,y1. и где-то в теле программы у вас есть код для двух функций. int Linija(int x1, int y1) { DrawLine(x1, y1); } и int Linija(int x0, int y0, int x1, int y1) { MovePenTo(x0, y0); DrawLine(x1, y1); }

    В программе вы можете вызвать Linija() с думя или четырьмя параметрами, а уж забота компилятора - как раз по числу параметров решить, какая версия будет вызываться.

    Другой способ использовать одно и то же имя для разных целей - это виртуальные (virtual) методы. КРАЙНЕ ВАЖНЫЙ для BeOS-программирования, и, несмотря на грозное и загадочное звучание - весьма простой способ. Важный - потому что практически вся обработка событий и сообщений в BeOS, включая рисование, производится с использованием виртуальных методов, а просто - потому что это просто надо привыкнуть:)

    Суть дела тут вот в чем, применительно к проблемам BeOS-программирования. Если перед объявлением любой функции класса стоит слово virtual,

    например:

    class KrutojeOkno : public BWindow
    {
    ....
    virtual int KnopkaNazhata();
    ...
    }
    

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

    class PrjamojeOkno : public KrutojeOkno
    {
    ....
    
    virtual int KnopkaNazhata();
    ...
    }
    

    при этом с совершенно другим кодом.

    Если бы мы так не сделали, то вызов PrjamojeOkno->KnopkaNazhata(); на самом деле бы запускал старую функцию, из класса KrutojeOkno, по праву наследования.

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

    В стандартном "системном" классе есть виртуальная функция MouseDown(int x, int y).

    Причем в этом самом классе - она пустая, по сути - заготовка.

    virtual void MouseDown(int x, int y)
    {
    }
    

    то есть не делает ничего, НО! - эта функция автоматически вызывается, когда пользователь щелкнул мышкой в окне. Причем вызывается с "правильными" параметрами - местом, где щелкнули.

    Грех не использовать такую простую, но ценную штуку в собственном классе - а как же еще мы может поймать мышиный щелчок в нашей программе? Но при этом мы можем снабдить ее осмысленным кодом:

    (отрывок заголовочного файла - здесь мы объявляем, что у нас будет своя версия этой функции)

    class VidVOkne : public BView
    {
    ...
    virtual void MouseDown(int x, int y, int buttons);
    ...
    }
    ----------------------
    (файл с кодом - здесь мы расписываем, 
    что наша версия делает) virtual void VidVOkne :: MouseDown(int x, int y) { DrawLine(x, y); }

    Ну вот, просто, но уже действенно - при нажатии на кнопку мыши в нашем окне будет рисоваться линия к месту щелчка. Для новичков в С++ на этом примере поясню, что когда вы объявляете класс, например внутри заголовочного *.h-файла (то есть описываете схематически, что у него внутри) - переменные и методы пишутся безо всяких "добавок", но когда уже идет код, то есть как методы этого класса "сделаны", например в *.cpp файле - перед собственным именем метода добавляется спереди название класса, к которому этом метод принадлежит - VidVOkne :: MouseDown.

    Ну а для тех, кто еще до сих пор в танке - фраза

    class VidVOkne : public BView

    означает, что наш класс VidVOkne будет наследником (со всеми потрохами) класса BView.

    Естественно, и код здесь слегка неполный, с точки зрения BeOS API, да и функции могут чуть не совпадать с описанными в BeBook - но идея должна быть понятна - вот тебе и виртуальные, но вполне себе действенные методы.

Теперь возвращаемся непосредственно к BeOS - API. На данный момент, чтобы создать и запустить что-либо видимое, нам не хватает пояснений по классу BWindow.

В тексте мы его уже упоминали, в связи с нашим классом KrutojeOkno, но безо всяких пояснений. Теперь вроде с пояснениями будет полегче, когда я скажу, что BWindow является, как и BApplication, наследником классов BLooper BHandler, соответственно), но боюсь, что еще не все новички-пчеловоды переварили порцию про классы и наследование. Поэтом на сей раз отмечу только факт, важный и для новичков и для гуру -

В самом BWindow НЕВОЗМОЖНО РИСОВАТЬ!!!.

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

Предназначение BWindow, кроме создания пустой рамки на экране:

1)Служить вместилищем-контейнером-родителем (parent) для других объектов-детей (child, children), которые и будут рисовать-петь-плясать и приносить нам кофе с утра в постель.

NB!!! BWindow не может быть добавлен к другому BWindow в качестве ребенка - окна в окне в BeOS не бывает !!!

2)Служить посредником-связником между этими объектами-детьми и системой. То есть ApplicationServer, InputServer и другими системными компонентами.

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

Для того, чтобы обеспечить как рисование, так и работу с сообщениями, в Application Server (см. часть 1 "Введения") создается "близнец" КАЖДОГО окна. Автоматически и невидимо для программиста. Впрочем, можно сказать, когда это происходит - в тот момент, когда в первый раз вызывается метод Show() класса BWindow.

Вот с ним-то, с этим близнецом на самом деле BWindow и общается, незаметно ни для пользователя, ни для программиста.

Впрочем, наблюдать этот факт крайне просто - сгрузите с Bebits программу ProcessController (если вы такой чудак, что еще не поставили ее), запустите. Поместите ее в Deskbar или на рабочий стол, щелкните на иконке и пойдите в раздел "Kill, Debug or Change Priority", там найдите app_server, плавно наведите на него курсор и, в раскрывшемся меню, вы увидите некоторое количество строк с "w::" в начале. Внимательно посмотрев, можно осознать, что все эти строчки с "w::" имеют соответствия на ваших рабочих столах, в виде открытых окон разных программ. Причем, если вы, в меню предыдущего уровня, наведете курсор не на app_server, а на какую-либо программу, то увидите, что там у каждой программы тоже прописаны свои соответствия этим окнам.

И хотя я об этом уже вроде говорил, повторить не мешает - BeOS - система сугубо, глубоко и автоматически многопоточная. В частности, эксперимент с ProcessController подтверждает тот факт, что каждое существующее окно - это даже не один, а два потока. Первый - в самой программе, второй - в сервере приложений.

За более подробный разбор BWindow и код простейшей программы придется взяться в следующий раз. Мозги-то у читателя не из хром-кобальта, могут и поизноситься от перегрузки, а нам, для полного счастья, еще не хватает пояснения-отступления про конструкторы-деструкторы, а также стеки и кучи.

Назад

Новости мира IT:

Архив новостей

Последние комментарии:

Релиз ядра Linux 4.14  (6)
Пятница 17.11, 16:12
Apple запустила Pay Cash (2)
Четверг 09.11, 21:15
Loading

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 985 1945361
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2015 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...