Главная » C++, C++ Builder » Расширения языка C++ в CBuilder

0

Программисты, работающие на традиционном C++ резко критикуют CBuilder из-за  того,  что фирма Borland воплотила его компилятор, применив несовместимые расширения языка. Однако если рассмотреть все аспекты, то окажется, что это весьма неубедительный аргумент. В конце концов, если вы хотите писать код на стандартном C++, вам ничто не мешает – CBuilder легко его обработает. Не хотите работать с расширениями, не надо – воля ваша. CBuilder без проблем работает с языком C++ стандарта ANSI, и на нем вы можете создавать полноценные приложения под Windows, не используя VCL. При желании вы можете сделать все, что я только что перечислил. Только зачем?

Главным аргументом против XE "VCL" использования VCL обычно является проблема XE "C++:проблема совместимости" совместимости. Действительно, вы не сможете работать с кодом вне среды CBuilder. Как ни странно, подобные упреки не адресуют  Visual Basic или Delphi, но фанатики C++ настаивают на совместимости, что на самом деле достаточно глупо. Вы не будете использовать CBuilder для создания мультиплатформенных приложений, вы используете его для создания самых лучших приложений под Windows в самый короткий срок.

Расширения

В этой главе мы рассмотрим те расширения XE "C++:расширения" стандартного языка  C++, которые делают CBuilder уникальным. Мы увидим, когда стоит их применять, а когда нет. Есть буквально несколько случаев, в которых вы действительно  должны применять расширения CBuilder, и мы рассмотрим их. Надо сказать, что C++ это и так достаточно сложный и вместе с тем мощный язык, так что не стоит использовать в своих приложениях больше  того,  что действительно  необходимо.

Давайте рассмотрим каждое расширение, используемое в CBuilder.

_asm и __asm

Ключевые слова, имеющие XE "ключевые слова:_asm и __asm" в своем составе XE  "__asm, ключевое слово " корень  asm, могут XE "_asm, ключевое слово " использоваться поочередно. Каждое из них (включая и не указанное в заголовке ключевое слово asm) просто-напросто предоставляет вам возможность поместить в исходный код вашего XE "ассемблер" приложения инструкции языка ассемблер не связываясь с ассемблерными модулями. Ключевое слово   asm уже   присутствует   некоторое   время   в   различных   компиляторах   фирмы   Borland   и   хорошо

зарекомендовало себя для использования со встроенным ассемблером. Как правило, вы будете использовать этот тип кода для увеличения скорости или доступа к системным  регистрам  и портам. Если вы до сих не умеете писать на ассемблере, могу сказать точно, что CBuilder – не лучший полигон для обучения.

__automated

Ключевое слово automated используется XE " ключевые слова:__automated" для свойств XE "__automated, ключевое слово " типа OLE automation в ваших XE "OLE automation" компонентах. Любое свойство, определенное как automated,  по  умолчанию  является  публичным  (public)  в вашем классе. Различие состоит в том, что автоматизированные свойства требуют для доступа к себе использовать функции-члены класса. Так что для взаимодействия с такими свойствами вы не сможете использовать просто переменные-члены класса. Кроме того, для этих функций класса вы должны использовать модификатор fastcall. Для автоматизированных свойств вы не можете использовать модификаторы index, stored, default или nodefault.

_cdecl и cdecl

XE " ключевые слова:_cdecl и __cdecl" Эти XE "_cdecl, ключевое слово " ключевые слова XE "__cdecl, ключевое слово " определяют, что функция или метод используют протокол стиля C; это означает, что функция компонуется с учетом регистра букв и добавлением подчерка (_) в начало имени функции. Использование этих вариантов так же влияет на то, как CBuilder передает переменные в стек. Последний параметр помещается в стек первым; за очистку стека ответственность несет блок кода, вызвавший эту функцию. Для использования функций или переменных cdecl в своей программе вам надо всего лишь использовать модификатор, и больше ничего. Остальное сделает за вас компилятор.

__classid

XE "функции:__classid" Функция classid используется XE "__classid, функция" для того, чтобы получить указатель на виртуальную таблицу класса в библиотеке VCL. Эта функции используется внутри самой системы CBuilder для работы с объектами и методами VCL, которые требуют для обозначения  объекта,  с  которым  работают,  использовать  указатель  this.  Несмотря  на  то,  что

__classid представляет из себя большую часть встроенной в CBuilder внутренней системы идентификации типов во время исполнения, это утверждение лучше не  использовать  в прикладных программах. Фирма Borland оставляет за собой право изменить поведение этой функции, так что все приложения, настроенные на какое-либо конкретное поведение компилятора, скорее всего не будут работать в следующих версиях. В качестве общего замечания скажу, что это выражение лежит в основе взаимодействия объектов C++ в CBuilder и VCL, основанной на языке Pascal.

__closure

XE "выражения:__closure" Выражение __closure XE "__closure, выражение" используется для деклараций функций обработки событий. Closure  – это специальный вид  XE "указатели:общие моменты" указателя на функцию, используемый в большинстве функций в библиотеках Windows. В отличие от обычных указателей на функции, эти содержат не только адрес вызываемой функции (четырехбайтный указатель), но так же и указатель на объект , для которого вызывается событие (указатель this). Использование выражения __closure некоторым образом  ограничивает возможности системы, так как при нем  возможно использовать лишь ограниченное число объектов одного класса. К счастью, поскольку это связано с указателем на адрес, число таких объектов весьма велико. В обозримом будущем вам нет нужды волноваться за количество объектов какого-нибудь класса в вашем приложении.

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

class MyClass

{

<  void ACallbackFunction( int x, double y, char *z );

}

<  // Определяем closure

<  void (__closure *CallbackEvent)(int, double, char *);

<  // Теперь можно сопоставить его объекту нужного класса

<  MyClass *obj = new MyClass;

<  CallbackEvent = obj->ACallbackFunction;

На самом деле при работе с обработчиками событий вы присваиваете и изменяете указатели на функции-члены класса. Несмотря на то, что работа с функциями класса это весьма неблагодарное занятие в C++ вообще, CBuilder использует лучшие стороны этой идеи в концепции использования выражения    closure.

__declspec

XE   "модификаторы:__declspec"   Модификатор

__declspec      XE   "__declspec,   модификатор"   в

CBuilder используется двумя независимыми способами. Во-первых это модификатор для импортируемых и XE "функции:DLL" экспортируемых функций в XE "DLL" DLL CBuilder. Это выражение   используется   вместо   имевшегося   в   более   ранних   компиляторах   модификатора

__export. У этого выражения было несколько слабых мест, основное – он должен был находиться всегда в фиксированной позиции в коде приложений. Например, нельзя было написать:

__export void func(void);

если вы хотели экспортировать функцию func. Вместо этого вы должны были писать: void __export func(void);

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

Модификатор __declspec ведет себя не так. Использовать его можно следующим образом: void __declspec(dllexport) funct(void);

__declspec(dllexport)void  funct(void);

Оба эти выражения допустимы и корректны. Оба они экспортируют функцию, называемую funct в DLL из CBuilder. Это выражение часто используется в прикладных программах и динамически подключаемых библиотеках, так что привыкайте использовать его в своем коде.

__except

XE "выражения:__except" Выражение __except эквивалентно XE "except, выражение"  выражению

except.   Оно   используется  при  обработке  исключительных     XE  "обработка  исключительных

ситуаций" ситуаций в С++ для определения действия, которое должно производиться при возникновении заданной ошибки внутри блока try. Основное различие между выражениями try … catch и __try …     except состоит в том, что try … catch используется в приложениях на C++, а __try

… __except используется в структурированных приложениях на C. Общий вид выражения except примерно следующий:

__try

{

<  // Некие выражения, которые могут повлечь за собой

<  // исключительную ситуацию

}

__except(someexpression)

{

}

Если вы пишите код для своих приложений в CBuilder только на C++, вам никогда не придется использовать выражения __try или __except. Тем не менее эти выражения могут использоваться в коде, унаследованном от С, который включается в  проекты  CBuilder.  Используя структурированную обработку исключительных ситуаций, вы работаете в рамках поддерживаемой 32-разрядными версиями Windows системы обработки, при этом вы добиваетесь совместимости с механизмом обработки исключительных ситуаций CBuilder, встроенным в VCL.

__export и _export

XE "модификаторы:_export и __export" Модификатор export XE "_export, модификатор:" используется XE "__export, модификатор" для экспорта классов, функций и данных внутри DLL CBuilder для использования в других приложениях. Есть несколько разновидностей этого модификатора, которые вы можете использовать в своих приложениях.

class __export MyClass

{

}

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

void __export Function(void)

{

}

Этот вид выражения экспортирует отдельную функцию C++ (или  С)  из  заданного  XE "DLL:экспорт функций" модуля DLL. Обратите внимание на то, что вы можете экспортировать любую (хоть все) функцию в DLL, при желании не предоставив пользователю доступа к внутренним функциям. Должен сразу же одно предупреждение относительно экспортирования функций из DLL  подобным образом. Вы экспортируете функцию по имени, а не порядковому номеру, а использовать порядковый номер гораздо эффективнее, да и загружается он быстрее. Так что описанный ваше вариант не подходит для случая, когда вас волнует скорость загрузки функций из DLL. Различие измеряется, естественно, всего лишь миллисекундами, но и это может

быть   заметно   в   высокопроизводительных   приложениях,   особенно   если   функция   из   DLL

вызывается много раз за небольшой промежуток времени. int __export nDataValue;

Последняя разновидность выражения __export используется для экспорта собственно XE "DLL:экспорт данных" значений данных из DLL в приложение. Конечно, вряд ли вы будете часто использовать подобное в своих приложениях, но иногда вам может понадобиться экспортировать флаги ошибок или переменные статуса.

Когда может возникнуть надобность в использовании выражения  export?  В  основном  в  том случае, если вы не захотите возиться с файлами описания модуля (DEF-файлами) и выражениями EXPORT в файле описания, которые определяют те части DLL, которые вы хотите сделать видимыми для других приложений в системе.

__fastcall и _fastcall

XE "модификаторы:_fastcall и fastcall" Одним XE "_fastcall, модификатор" из наиболее XE "__fastcall,   модификатор"   важных   расширений   в   системе   CBuilder   является   модификатора

__fastcall, которое делает возможным использовать основанные (и написанные) на  языке  XE "Pascal" Pascal объекты VCL в ваших XE "Delphi и CBuilder:синтаксис" приложениях на C++. Использование  модификатора  fastcall  дает  инструкцию  компилятору  сгенерировать  код, который передает параметры в функции через системные регистры, то есть также, как это предполагается в Pascal.

Правила XE "__fastcall:правила исполдьзования" использования __fastcall весьма просты. Если вы экспортируете из своего класса метод, который должен быть использован в Object Inspector или где-нибудь еще, в описании функции вы должны использовать модификатор __fastcall. Если вы замещаете метод VCL, который определен с использованием модификатора fastcall вы так же должны использовать этот модификатор при описании вашего метода. Поскольку __fastcall не наследуется, вы должны указывать его на каждом  уровне  класса,  который  осуществляет замещение подобного метода.

Например, если вы замещаете метод Paint компонента в класса, наследующем от объекта VCL, вам надо написать следующее:

virtual void     fastcall Paint(void);

Это необходимо, поскольку Paint на самом деле написан на языке Pascal в объекте VCL. С другой стороны, если вы хотите воплотить в вашем классе  новую функцию,  которая  вызывает объект VCL низкого уровня, не нужно использовать модификатор    fastcall для нового метода.

Представьте, например, что вам надо создать новый метод, который бы переключал состояние формы видима/невидима (Hide/Show), даже не зная текущего состояния. Метод, который мы назовем Toggle, может быть задекларирован следующим образом:

virtual void Toggle(void);

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

void  TMyComponent::Toggle(void)

{

<  if ( Visible ) Hide();

<  else

Show();

}

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

__finally

XE "выражения:__finally" Выражение finally используется XE "__finally, выражение" вместе с выражением        try    для     обработки     исключительных     ситуаций,     используя     для     этого

структурированные    исключительные    ситуации.    Поскольку    выражения    __try    и

__finally

используются только в файлах и функциях C, нет нужды волноваться по их поводу в CBuilder,

поскольку эти выражения не будут использованы внутри IDE.

Для использования внешних функций C и структурированной  обработки  исключительных ситуаций, предоставляемой CBuilder и языком C++ вам придется использовать версию компилятора в командной строке.

_import и   import

XE "выражения:_import и import" Целью выражения import в CBuilder XE "_import, выражение" является определение XE "__import, выражение" того, что класс, функция  или  блок  данных должен быть импортирован из внешнего источника. Вы можете импортировать определенные классы из внешних DLL или библиотек, так же, как и функции на языке C. Используя одну из разновидностей выражения об импорте, элементы данных, определенные во внешних DLL могут восприниматься также, как если бы они были частью программы, в которой их используют.

Выражения _import и import используются как модификаторы для типов импортируемых элементов. Существует три вида этого выражения, каждый – для одного из типов, которые могут быть импортированы подобным образом:

class _import MyClass

{

}

Это выражение XE "импорт классов:" определяет импортируемый класс. XE "классы:импортирование" CBuilder сгенерирует весь код, необходимый для того, чтобы загрузить класс из подключенной DLL (естественно, только если он будет там найден), так что вы можете просто определять экземпляры этого класса в вашем приложении.

int __import MyFunction(int arg1);

Это выражение определяет функцию, определенную где-то вовне – в библиотеке или DLL, которая принимает одно целое число в виде аргумента, а другое возвращает. Отметьте, что модификатор

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

int _import MyData;

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

__int8,     int16,     int32 и __int64

XE "выражения:__intX" Эти выражения XE "__intX, выражения" позволяют определить размер целых чисел для переменных в системе. Число, следующее за __int обозначает количество битов, которое переменная будет занимать в памяти. К  примеру, тип переменной int16 определяет переменную, занимающую в памяти 16 битов (стандартное целое в Windows).

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

__int8 iVal = 9;

_pascal и   pascal

XE " ключевые слова:_pascal и __pascal" Ключевое XE "_pascal, ключевое слово " слово __pascal определяет XE "__pascal, ключевое слово " метод передачи данных в функции и методы в системе CBuilder. Соглашения вызовов в стиле XE "Delphi и CBuilder:конвенции вызова" Pascal имеют два заметных отличия от стандартных вызовов в стиле С. Во-первых, функции XE "функции:языка Pascal" Pascal не XE "Pascal" различают регистр букв, так как все они на самом деле хранятся в виде заглавных букв. Это позволяет компилятору без труда подключать новые модули в систему. В С++ функции стиля Pascal все еще калечатся, но имена функций преобразуются  в  верхний регистр.

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

__property

XE "модификаторы:__property" Одним из XE "__property, модификатор" наиболее важных новых модификаторов можно считать выражение __property. Это XE "свойства:синтаксис" выражения служит для того, чтобы показать компилятору, что он имеет  дело  со  свойством объекта  стиля VCL. Свойства предоставляют специальные функции read и write для доступа к хранящимся в них данным. Использование модификатора __property показывает компилятору, что прямой доступ к свойству возможен не всегда, как это показано в следующем примере:

private;

int FMyInt; public;

void __fastcall SetInt(int iNewInt);

__published;

__property int MyIntProperty = {read=FMyInt, write=SetInt};

В данном случае мы показываем, что пользователь имеет право читать свойство, получая при этом значение внутренней переменной класса FMyInt. При попытке записи нового значения свойства компилятор по-тихому вызовет метод объекта, названный SetInt. Ниже приведен некий код, который поможет вам лучше понять эти моменты:

TMyObject *pObject = new TMyObject (NULL); // Создаем

// экземпляр

pObject->MyIntProperty = 164; // Вызов SetInt

int nInt = pObject->MyIntProperty; // Получаем значение

<  // переменной класса FMyInt

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

__published

XE  "  ключевые  слова:__published"  Ключевое  слово

__published  используется  XE  "__published,

ключевое слово " для того, чтобы дать знать компилятору о своем желании видеть некоторые свойства данного объекта отображенными в Object Inspector. Секцию published могут содержать только объекты, наследующие от TObject. В отличие от многих других выражений и модификаторов, выражение published относится не к одной строке, а может рассматриваться как выражение определения прав доступа (как и private, protected или public) и определять свойства объекта, которые будут являться публичными, и при этом еще и отображаться в окне Object Inspector. Использование этого выражения приведет так же к тому, что информация об объекте, содержащем выражение __published, будет содержать данные времени исполнения об XE "RTTI" идентификации типов (Runtime Type Identification, RTTI), представленные в стиле  Delphi;  что может быть использовано для формирования запросов к объекту о существующих в нем свойствах и методах.

__rtti

XE " ключевые слова:__rtti" Также как и __published, XE "rtti, ключевое слово " выражение __rtti принуждает компилятор создавать идентификацию времени исполнения для класса  или структуры. До тех пор, пока вы не велите ему делать обратное, компилятор по умолчанию будет генерировать информацию XE "RTTI" RTTI. Вы можете использовать флаг командной строки – RT- для того, чтобы компилятор не создавал информацию RTTI о программе и ее данных в целом. Если вы так сделаете, но при этом захотите, чтобы некоторые классы или структуры в программе использовали RTTI, вам следует использовать модификатор __rtti, применяя  следующий синтаксис:

struct     rtti MyStructure {};

При этом структура MyStructure будет сгенерирована с данными времени исполнения об идентификации типов, после чего можно запросить эту информацию, используя в  своем приложении классы typeinfo и typeid.

__thread

XE "модификаторы:__thread" Модификатор __thread используется XE  "__thread, модификаторр" для объявления глобальных переменных, уникальных для каждого потока. Например, если  вы хотите завести отдельную глобальную переменную в качестве флага, показывающего, загружен ли или нет некий данный файл, вам стоит использовать логическую глобальную переменную. Однако эта переменная может различаться в различных потоках; каждый поток может нуждаться в загрузке своей версии файла. Файл может быть так же и протоколом работы, при этом вовсе не обязательно, что все потоки должны писать в один и тот же протокол. В этом случае вам потребуется флаг этого протокола, уникальный для каждого потока, в котором он используется. Для этого вам и надо будет использовать модификатор __thread для переменных.

Синтаксис модификатора    thread имеет следующий вид: int __threadbFileOpen = FALSE;

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

int __thread nNumTimes = GetNumberOfTimes();

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

__try

Так же как и except, XE "__try, выражение" выражение try является вариантом обработки исключительных ситуаций, используемым только в программах на С. При работе с программами, использующими С++ (такими, как CBuilder), используйте вместо него выражение try. Для получения дополнительной информации по использованию выражения try смотрите статью, посвященную выражению    except.

Источник: Теллес М. – Borland C++ Builder. Библиотека программиста – 1998

По теме:

  • Комментарии