Продолжается подписка на наши издания! Вы не забыли подписаться?

Tiburon – Delphi/C++Builder 2009

Автор: Александр Полозов
Опубликовано: 28.04.2009

Вступление

Бывают релизы, которых ждёшь не просто с нетерпением, а просто с какой-то жаждой. Релизы, которые круто поворачивают жизнь людей, которым приходится работать с этим ПО. А если речь идёт ещё и о разработке ПО – тогда новый релиз, несущий свежие возможности и нескольколетний труд целой компании, всегда воспринимается как нечто благоговейное. Выпущенные в конце августа Delphi 2009 и C++Builder 2009, известные доселе под кодовым названием Tiburon (исп. «акула»), мир программистов склонен причислять именно к подобным релизам.

Сама компания CodeGear, а также её новый хозяин – корпорация Embarcadero – склонна употреблять в анонсах выражение «breaking release». Здесь имеет место игра слов: «breaking» можно трактовать, с одной стороны, как указание на то что переход на новую версию требует специального портирования, и часть кода не сможет удовлетворять новым требованиям языка, а с другой – как краткое описание всего того поистине «взрывного» набора возможностей, которые даёт нам этот замечательный инструмент. В самом деле, список нововведений поражает: полный переход на Unicode, введение в язык параметризированных типов (generics), анонимных функций, многочисленные развития VCL, поддержка нового стандарта С++0x... да что перечислять, список растянется надолго. Так что в этой статье я и постараюсь основательно пройтись по всем основным возможностям Тибурона и показать, чем же на самом деле объясняется это громкое слово «breaking».

Unicode

Разговор о полной поддержке Unicode в Delphi 2009 давным-давно новостью не является. Краем уха или глаза, так или иначе, это известие стало известно всем, кому можно, за каких-то два месяца. Однако подробности и тонкости такого перехода описывались редко и отрывками, причём в разных источниках. Здесь я попытаюсь до мелочей расписать всю подноготную использования Unicode в Тибуроне.

Фундаментальные сведения

Итак, Unicode теперь поддерживается везде. Совершенно везде. VCL, RTL, среда, компилятор, control-ы, библиотеки БД (dbExpress etc.), COM, Web-control-ы... все полностью по умолчанию в Unicode и отлично настраивается с точки зрения кодировок. Поясняю. Как известно, в Delphi 1 строковый тип был ещё Турбо-Паскалевским ShortString (который представлял собой буфер фиксированного размера с длиной в первом символе строки и однобайтным представлением символов – прим.ред.). Начиная с Delphi 2 и до настоящего момента string по умолчанию был Windows ANSI-строкой:

type
  string = AnsiString;

То же касается PChar, который по факту означал PAnsiChar, и т.д. Была также COM-версия Unicode-строки – WideString, эквивалент для С++ BSTR, более медленная, так как для ее размещения использовался менеджер памяти СОМ, а не быстрый менеджер памяти Delphi, и вдобавок ещё не везде допустимая. Но теперь всё изменилось. Теперь по умолчанию:

type
  string = UnicodeString;
  Char = WideChar;
  PChar = PWideChar;

UnicodeString – это новый тип, экземпляры которого управляются путем подсчета ссылок, хранящий строку в формате UTF-16. Char отображается на WideChar, так что Char теперь стал по умолчанию 16-битным (UTF-16). Присваивание UnicodeString переменной типа WideString приводит к автоматическому преобразованию строк.

Давайте внимательнее посмотрим на AnsiString:

type
  UTF8String = type AnsiString(CP_UTF8);
  RawByteString = type AnsiString($FFFF);

В круглых скобках для типа AnsiString (и только для него) отныне указывается кодовая страница (codepage) в международном стандарте: от $0000 до $FFFF. Так, к примеру, классическая кириллическая кодировка Windows – это $1251 (она же CP1251, Windows-1251, и т.д.). А CP_UTF8 в вышеприведённом коде, как легко догадаться, не более чем системная константа:

const
  CP_UTF8 = $65001;

Полный обзор кодовых страниц можно найти во многих источниках, например, на MSDN.

Как известно, если при определении нового типа через существующие мы используем в правой части выражения дополнительное ключевое слово type, то это значит, что между экземплярами исходного и порождаемого типа будет поддерживаться автоматическое преобразование (конвертация). Преобразование будет осуществляться при присваивании значения одного типа переменной другого. Отныне при любом присваивании любой строки другой строке производится проверка их кодировок и автоматическая конвертация в случае несовпадения. В случаях же, когда эта конвертация может вызвать потенциальную потерю данных (к примеру, UTF-16 ? CP1252), генерируется соответствующее предупреждение. Все присваивания, дабы максимально уберечься от потери данных, проходят через UnicodeString, то есть через UTF-16, потому что соответствующие функции для преобразования входят в WinAPI, а функция перехода между двумя произвольным кодировками – нет. Но всё-таки это не панацея.

Знакомая вам по прежним версиям работа со строками совершенно не изменилась. Всё так же строки состоят из символов, символы прекрасно доступны через индекс, начиная с единицы, всё так же изменяемы и форматируемы.

Библиотека времени исполнения (RTL)

Какие же нововведения, связанные с поддержкой Unicode, появились в RTL? Информация в данной части статьи практически целиком взята с Developer Network. Любознательные читатели вполне могут ознакомиться с ней в оригинале – ссылки будут даны ниже.

Во-первых, мы получили модуль Character со статическим классом TCharacter. Он даёт нам множество Unicode-функций – для проверки символов (такие, как TCharacter.IsLetter(), TCharacter.IsDigit()), то же для проверки суррогатных Unicode-символов и т.д. Часть из них доступны и как независимые функции в модуле.

Во-вторых, в наличии имеется статический класс TEncoding, основное предназначение которого заключается в следующем наборе членов:

class property ASCII: TEncoding read GetASCII;
class property BigEndianUnicode: TEncoding read GetBigEndianUnicode;
class property Default: TEncoding read GetDefault;
class property Unicode: TEncoding read GetUnicode;
class property UTF7: TEncoding read GetUTF7;
class property UTF8: TEncoding read GetUTF8;
class function GetEncoding(CodePage: Integer): TEncoding;

Он необходим в тех местах, где теперь надо явно указать кодировку, например, в потоках, списках строк и т.д.

ListBox1.Items.SaveToFile('MyListBoxItems.txt', TEncoding.UTF8);

Стоит здесь ещё отметить, что в System.pas присутствует исключение EEncodingError.

И, конечно же, все control-ы dbExpress также приобрели к себе в набор новое свойство Encoding.

В-третьих, мы получили отличный дотнетовский TStringBuilder, с ускоренными операциями модификации строки (Append, Insert, Delete, Replace, и т.д.), возвращающими получившийся экземпляр того же TStringBuilder, так что теперь имеет право на жизнь следующая конструкция:

procedure TForm1.Button1Click(Sender: TObject);
var
  MyStringBuilder: TStringBuilder;
  Price: double;
begin
  MyStringBuilder := TStringBuilder.Create('');
  try
    Price := 1.49;
    Label1.Caption := MyStringBuilder.Append('The apples are $').Append(Price).Append(' a pound.').ToString;
  finally
    MyStringBuilder.Free;
  end;
end;

В-четвёртых – полезные мелочи. К ним я отнес бы функции StringElementSize(), StringCodePage(), BytesOf() и процедуру SetCodePage. Их предназначение понятно из названий: соответственно, "получить размер одного типичного символа данной строки", "получить кодовую страницу данной строки", "получить строку в виде массива байт" и "установить кодовую страницу данной строки" (с дополнительным логическим параметром "конвертировать прямо сейчас"). Всё просто и удобно.

Материалы для дополнительного чтения

http://www.youtube.com/watch?v=BJMakOY8qbw,

http://www.youtube.com/watch?v=n2HcdCV6kfk,

http://www.youtube.com/watch?v=BkEi9GI8cp4l,

http://www.youtube.com/watch?v=pg8rWX_rbRA,

http://www.youtube.com/watch?v=cTHEt_3n5fw

Generics

Про generics (они же дженерики, они же обобщения, одним словом, поддержка параметрического полиморфизма) на просторах программистского сообщества в контексте Delphi было сказано уже немало. Впервые о данном явлении в Delphi-сообществе заговорили в 2004-2005 годах, когда Microsoft выпустила на свет .NET 2.0, а Sun – J2SE 5.0, и появились первые mainstream-языки, которые были в состоянии состязаться с зарекомендовавшей себя годами концепцией шаблонов С++. Прошло время, осенью 2007 была выпущена CodeGear RAD Studio 2007, включающая в себя Delphi.NET под Framework 2.0, с поддержкой generic-ов. Тогда разработчики нам пообещали, что в следующей же версии эта возможность будет доступна и в Win32-версии Delphi.

Принцип обобщения

Сначала скажу о том, что же, собственно говоря, представляют собою generic-и. Это – класс, запись, интерфейс либо метод, зависящий от типа. То есть (рассуждая на примере класса), мы имеем класс, на этапе разработки которого неизвестно, с каким типом придётся "иметь дело", однако, независимо от этого, класс реализует общее, абстрактное поведение для любого типа, который удовлетворяет определённым ограничениям. Если вы пишете сортировку, то какая разница, сортируются ли числа, строки или автомобили? Нам важно только, чтобы для данного типа была реализована или перегружена операция сравнения. Более того, если передать сортировщику ссылку на функцию сравнения (кстати, см. ниже об анонимных методах), то даже и от сравнения можно абстрагироваться. Второй пример: контейнеры. Список, стек, очередь, словарь, набор (set), разнообразные деревья хранят в себе экземпляры определённого типа... какая разница, какого? От них требуется общее поведение: добавлять данные, хранить данные, удалять данные, искать данные. Но довольно слов, перейдём к делу.

Вот пример объявления обобщённого списка:

TList<T> = class
private
  FItems: array of T;
  FCount: Integer;
  procedure Grow(ACapacity: Integer);
  function GetItem(AIndex: Integer): T;
  procedure SetItem(AIndex: Integer; AValue: T);
public
  procedure Add(const AItem: T);
  procedure AddRange(const AItems: array of T);
  procedure RemoveAt(AIndex: Integer);
  procedure Clear;
  property Item[AIndex: Integer]: T read GetItem write SetItem; default;
  property Count: Integer read FCount;
end;

Здесь мы описали класс TList, который зависит от одного определённого типа элементов, который там будет храниться. Поскольку мы не знаем на этапе написания, какой это будет тип, он условно назван Т. Теперь Т в контексте данного класса есть идентификатор некоторого типа, а за его пределами – не имеет никакого смысла.

А как таким пользоваться? Очень просто.

var
  ilist: TList<Integer>;
  slist: TList<String>;     
  IntElem: Integer;
  StrElem: String;

begin
  ilist := TList.Create;
  try
    ilist.AddRange([1, 2, 3]);
    for IntElem in ilist do
      Write(IntElem, ' ');
    ilist.Clear;
  finally
    ilist.Free;
  end;

  slist := TList.Create;
  try
    slist.AddRange(['one', 'two', 'three']);
    for StrElem in slist do
      Write(StrElem, ' ');
    slist.Clear;
  finally
    slist.Free;
  end;

  Readln;
end.

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

Различные параметризированные конструкции

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

type
  // Обобщённый метод не обобщённого класса
  TFoo = class
    procedure GenericMethod<T>(aParam: T);
    function GenericFunction<T>: T;
  end;

function TFoo.GenericFunction<T>: T;
begin
  Result := Default(T)
end;

procedure TFoo.GenericMethod<T>(A: T);
begin
  Writeln(TObject(aParam).ToString);
end;

var
  Foo: TFoo;
begin
  Foo := TFoo.Create;
  Foo.GenericMethod<integer>(42);
  Foo.GenericMethod(13);
  Foo.Free;
end;

Два слова о функции Default(). Как легко догадаться, не зная, что представляет собою тип, мы не можем и догадываться о возможных его значениях. Функция Default() помогает, когда требуется указать значение по умолчанию для этого обобщённого типа. Для целого типа она вернёт 0, для типа с плавающей точкой – 0.0, для строки – пустую строку (''), для указателя или класса – nil, для записи – экземпляр записи с обнулёнными подобным же образом полями и так далее.

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

type
  TGenericClass<T, U> = class
    procedure GenericMethod<V>(aParam1: T; aParam2: U; aParam3: V);
  end;

procedure TGenericClass<T, U>.GenericMethod<V>(aParam1: T; aParam2: U; aParam3: V);
begin
  Writeln(TObject(aParam1).ToString);
  Writeln(TObject(aParam2).ToString);
  Writeln(TObject(aParam3).ToString);
end;

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

Ограничение параметров типов (type parameter constraints)

Иногда есть потребность не просто указать, что обобщение зависит от типа, а ещё и уточнить, с какими типами его можно использовать, а с какими – нельзя. К примеру, если внимательно посмотреть на примеры кода в предыдущем подразделе, то станет ясно, что они будут стопроцентно рабочими только в Delphi.NET. Одним из отличий Delphi for Win32 является то, что примитивные типы не являются ссылочными (хранятся по значению), следовательно, приведение TObject(aParam) корректно далеко не всегда, а только если aParam – объект, то есть, другими словами, T – классовый тип. Придётся так и записать:

type
  TGenericRecord<T: class> = record
    procedure DoSomething(aParam: T);
  end;  

procedure TGenericRecord<T>.DoSomething(aParam: T);
begin
  Writeln(TObject(aParam).ClassName);
end;

Записью "T: class" я конкретизировал, что generic TGenericRecord принимает только классовые типы, то есть при попытке объявить нечто вроде

var
  WrongOne: TGenericRecord<Real>;

компилятор остановится с ошибкой, а вот

var
  CorrectOne: TGenericRecord<TFont>;

уже пропустит без единого слова.

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

type  
  TConstrainedGeneric<
    T;  // Без ограничений
    U: class;  // Классовый тип
    V: constructor;  // Тип, имеющий конструктор без параметров
    W: record; // Тип-значение (value type, например, record или integer)
    X: TFoo;  // Наследник данного класса или сам он
    Y: class, IMyInterface;  // Класс, реализующий указанный интерфейс
    Z: IGenericInterface<T>  // Обобщённый интерфейс, зависящий от T
  > = class
  end;

Ограничения в Delphi 2009 – почти полная копия ограничений .Net, что совершенно логично, если вспомнить обещания разработчиков реализовать generic-и .NET в Delphi для Win32.

Так, record означает то же самое, что в C# – struct, то есть тип, подставляемый в этот параметр типа, обязан быть типом-значением. Тип-значение подразумевает, что это может быть любой тип, кроме ссылочных. Например, это может быть целое, запись (record), Real и.т. Но он не может быть строкой или классом, потому что это ссылочные типы.

Существенная проблема generic-ов – это отсутствие ограничения на перегруженный оператор. То есть, переменные неизвестного на момент разработки типа невозможно складывать, умножать, присваивать, сравнивать и т.д. Такая ситуация (кстати, полностью соответствующая положению дел в .Net) связана с тем, что в Delphi, как и в .Net, generic-и являются сущностями времени исполнения (как, впрочем, и разработки). В отличие от шаблонов C++, generic-и компилируются в специальную промежуточную форму, помещаются в исполняемые модули и могут быть использованы без наличия исходного кода как в других проекта, так даже и во время исполнения программы.

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

С другой стороны, если взглянуть на реализацию перегрузки операторов в Delphi, то становится ясно, что generic с ограничением на операторы – задача незаурядная (впрочем, решенная, например, в F#, где ограничения могут быть на отдельный метод, в том числе и на операторы – прим.ред.). Сложно себе представить нечто вроде:

Type
  TGenericWithAdd<T: class, constructor, operator Add<T,U>(T; T): U, 
    operator Implicit<T,U>(U):T> = class;

Материалы для дополнительного чтения

Ну и на закуску как постскриптум. Реализовав в Delphi for Win32 generic-и, команда CodeGear, естественно, не ограничилась этим, и добавила их широкое использование в RTL и VCL. В частности, в модуле Generics.Collections мы теперь увидим обобщённые версии TArray, TList, TStack, TQueue, TDictionary, TObjectList, TObjectQueue, TObjectStack, TObjectDictionary и TEnumerator.

Анонимные методы

К сожалению, по неведомой причине разработчики Delphi, повторили ошибку разработчиков из Microsoft. Вместо того чтобы сразу взять замечательный краткий синтаксис лямбд из функциональных языков (таких, как Haskell), они создали подобие анонимных методов из C# 2.0. Разница между ними такова: по сути это одно и то же, но внешне они кардинально отличаются. Вот анонимный метод Delphi:

function(x : Integer) : Integer
begin
  Result := x * x
end;

А вот аналогичная лямбда из Haskell:

\x -> x * x

И лямбда-выражение из C# 3.0:

x => x * x

Нетрудно заметить, что вся разница заключается в громоздком синтаксисе и отсутствии выведения типов у анонимного метода. К сожалению, эта «небольшая» разница кардинально влияет на реальную применимость данной конструкции в коде (что прекрасно демонстрирует практика применения C# 2.0 и 3.0). Однако с точки зрения возможностей и анонимные методы, и лямбды идентичны. Это позволяет применять функциональный стиль программирования в Delphi. На самом деле, это намного больше, чем то, что есть сейчас в C++, где лямбды отсутствуют, а их эмуляция на шаблонах вызывает уныние.

Стоит заметить, что во многих широко распостранённых библиотеках C++ (таких, как boost и TR1, которые будут описаны ниже) синтаксис и функциональность лямбд внедрены в язык с помощью макросов препроцессора. Решение резко замедляет процесс компилирования кода и отличается понижением скорости разработки, однако вполне имеет право на жизнь.

Вкратце поясню, что же такое анонимные методы. Анонимный метод – это процедура или функция, объявленная «по месту», то есть внутри кода другой процедуры/функции, и не имеющая имени. Анонимный метод можно назначить переменной или назначить в качестве параметра. Кроме того, тела анонимных методов могут ссылаться на переменные внешней процедуры/функции, видимые в точке объявления анонимного метода.

Пожалуй, лучше всего объяснит детали маленький кусок кода:

type
  // указатель на метод
  TProc = reference to procedure(x: Integer);

procedure Call(const proc: TProc);
begin
  proc(42);
end;

var
  proc: TProc;
begin
  // анонимный метод
  proc := procedure(a: Integer)
          begin
            Writeln(a);
          end;               

  Call(proc);
  Readln;
end.

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

Вот расширенный пример применения ссылки на процедуру.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TProc = reference to procedure(x: Integer); // указатель на метод

  TMyClass = class
    public x : Int32;
    public procedure Test(a: Integer);
  end;

procedure TMyClass.Test(a: Integer);
begin
  Writeln('Hello from method "Test"!');
  Write('Value of field "x" is ');
  Writeln(x);
  Write('Value of parameter is ');
  Writeln(a);
end;

procedure Call(const proc: TProc);
begin
  proc(42);
end;


var
  proc: TProc;
  x : TMyClass;
  y : Int32;
begin
  // анонимный метод
  y := 567;
  proc := procedure(a: Integer)
          begin
            Writeln('Hello from anonumous method!');
            Write('Value of local variable "y" is ');
            Writeln(y);
            Write('Value of parameter is ');
            Writeln(a);
          end;

  Call(proc);

  Writeln('');

  x := TMyClass.Create;
  x.x := 123;
  Call(x.Test);
  Readln;
end. 

Этот код выводит на консоль:

Hello from anonumous method!
Value of local variable "y" is 567
Value of parameter is 42

Hello from method "Test"!
Value of field "x" is 123
Value of parameter is 42

Как видите, одна и та же процедура Call() получает в качестве параметра как ссылку на анонимный метод, так и ссылку на метод объекта обычного класса. Этот код интересен и тем, что в нем используется так называемый «захват контекста» (см. переменную «y»). Вот о нем-то и пойдет речь далее.

Захват контекста

Теперь поговорим про контекст. Если бы всё, что относится к анонимным методам, заканчивалось на описанном выше, тогда они превращались бы в полнейшую бессмыслицу, допустимую только для функций вроде A+B. Но захват контекста придаёт им невиданную (для императивных программистов) мощь функционального программирования.

Для анонимных методов видимы все те идентификаторы, которые были видимы (и доступны для использования) в месте их добавления (т.е. в лексической области видимости).

В функциональных языках захват лексического контекста называется лексическим замыканием (lexical closure).

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

type
  TFuncOfInt = reference to function(x: Integer): Integer;

function MakeAdder(y: Integer): TFuncOfInt;
begin
  MakeAdder := function (x: Integer): Integer;
               begin
                 Result := x + y;
               end;
end;

var
  Adder: TFuncOfInt;

begin
  Adder := MakeAdder(20);
  WriteLn(Adder(22));  // выведет 42
  Readln;
end.

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

Захват контекста позволяет значительно проще решать многие задачи, не создавая при этом множества маленьких классов, а используя динамически создаваемые замыкания, хранящие необходимое состояние. Например, анонимный метод с замыканием может быть очень удобен при обработке обратных вызовов (callback).

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

type
  TAnonymousThread = class(TThread)
  public
    procedure Execute(executor: TProc); overload;
  end;

//  blah blah

procedure TForm1.Button1Click(Sender: TObject);
 var
   Thread: TAnonymousThread;
Begin
  Thread ? TAnonymousThread.Create;
  Thread.Execute(
    procedure;
    var
      I: Integer;
      Sum: Int64;
    begin
      Sum:= 0;
      for I := 1 to 1000000 do begin
        Inc(Sum, I);
        Synchronize(
          procedure;
          begin
            Caption := IntToStr(Sum);
          end;
        );
      end;
    end;
  );
  Thread.Free;
end;

Этот код демонстрирует сразу нескольких преимуществ анонимных методов. Я всего лишь парой строк отправил долго выполняемый код в отдельный поток, не создавая для этого никаких отдельных процедур, не мучаясь с событиями и присваиваниями, сохранив удобочитаемость кода и избавив от необходимости постоянно перепрыгивать в окне редактора от одной подпрограммы к другой. Как пишется, так и читается. Кроме того, здесь показан новый перегруженный вариант метода Synchronize, который принимает ссылку на метод без параметров (куда, в данном случае, передается анонимный метод). Аналогичный перегруженный вариант метода Execute приходится написать самостоятельно.

В этом разделе в примерах часто упоминаются такие типы, как TProc, TFunc, TPredicate. Собственно говоря, это стандартные обобщенные типы – ссылки на фунции и процедуры (в случае с TProc) описанные в SysUtils. CodeGear дала стандартные названия методам, принимающим до четырёх параметров произвольных типов включительно. Вот их описание из SysUtils.pas:

type
  TProc = reference to procedure;
  TProc<T> = reference to procedure (Arg1: T);
  TProc<T1,T2> = reference to procedure (Arg1: T1; Arg2: T2);
  TProc<T1,T2,T3> = reference to procedure (Arg1: T1; Arg2: T2; Arg3: T3);
  TProc<T1,T2,T3,T4> = 
    reference to procedure (Arg1: T1; Arg2: T2; Arg3: T3; Arg4: T4);

  TFunc<TResult> = reference to function: TResult;
  TFunc<T,TResult> = reference to function (Arg1: T): TResult;
  TFunc<T1,T2,TResult> = reference to function (Arg1: T1; Arg2: T2): TResult;
  TFunc<T1,T2,T3,TResult> = 
    reference to function (Arg1: T1; Arg2: T2; Arg3: T3): TResult;
  TFunc<T1,T2,T3,T4,TResult> = 
    reference to function (Arg1: T1; Arg2: T2; Arg3: T3; Arg4: T4): TResult;

  TPredicate<T> = reference to function (Arg1: T): Boolean;

Более подробно о том, что же такое анонимные методы, и зачем они нужны, рассказал в своём блоге Ларс Фосдэл, ссылка на него приведена ниже.

Лямбда-выражения

В C# 3.0 появился упрощенный синтаксис анонимных методов под названием «лямбда-выражения». Их использование в сочетании с новой библиотекой LINQ (Language Integrated Query), появившейся в .NET Framework 3.5, позволяет существенно упростить обработку данных (как находящихся в памяти компьютера, так и хранящихся в БД). Вот пример кода, отбирающего из коллекции customers покупателей, проживающих в городе «Осло», сортирующиего результат по имени и возвращающего список имен:

var result = customers.Where(c => c.City = 'Oslo')
                      .OrderBy(c => c.Name).Select(c => c.Name);

Не правда ли, очень кратко и выразительно? В данной строчке любой заметит явную аналогию с SQL-запросом:

select c.Name from customers c where c.City = 'Oslo' order by c.Name

К сожалению, в Delphi разработчики остановились на введении анонимных методов и не пошли дальше – компактный синтаксис лямбда-выражений пока так и не появился. Будем надеяться, что в следующей версии Delphi разработчики предоставят нам эту замечательную возможность, плюс, возможно, добавят в язык поддержку LINQ.

В любом случае наличие анонимных методов и функциональных типов даёт широчайший простор любителям функционального программирования. Не смею отбирать лавры у Крэйга Станца с его восхитительным примером итератора по простым числам с помощью рекурсивных анонимных методов и просто помещу ссылку на эту статью в «материалах для дополнительного чтения» на первом месте.

Материалы для дополнительного чтения

IDE и VCL

Здесь материала становится на порядок больше, а объём каждого отдельного пункта – на тот же порядок меньше. Что логично: нововведений в IDE и VCL достаточно много, однако большую часть из них достаточно назвать и описать, а не расписывать, как в случае с тремя вышеприведёнными глобальными нововведениями. Да, и материалы и ссылки здесь даются после каждого из отдельных подпунктов.

Первое, о чём стоит предупредить в этих строках. Тибурон – это два отдельных продукта: Delphi 2009 и C++ Builder 2009. Будучи установленными вместе на одну машину, они объединяются в привычную для нас интегрированную среду, однако ничто не мешает пользоваться ими по отдельности. Что же касается Delphi for .NET, то её поддержка ничуть не прекратилась – просто релиз будет примерно через полгода, с абсолютно ошеломляющими нововведениями, о которых разработчики говорят лишь туманными намёками.

Компоненты: новое и старое

Для начала о мелких новых компонентах и усовершенствованиях в старых, куда же без них. В принципе, первый пункт практически полностью можно свести к нескольким скриншотам с Developer Network, приведённым ниже. Я же просто добавлю краткое текстовое описание каждой из составляющих.

TCategoryPanel. Коллекция раздвижных панелей в стиле Outlook, занимает всю левую половину первого скриншота. Можно настраивать положение текста на заголовках панелей, цвета и градиенты заголовков, их расположение (вертикальное/горизонтальное) и др.

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

TLinkLabel. Обычный label, просто умеющий распознавать HTML-тэг <a>, и с добавленным событием OnLinkClick. Незатейливый вид – в центре справа первого скриншота.

TBalloonHint. Vista-like широко настраиваемая всплывающая подсказка с картинкой, на остальных ОС идентична традиционной. Эффект показа – в правом нижнем углу первого скриншота.

Во всех визуальных компонентах теперь присутствует свойство CustomHint, которому можно назначить любого наследника TCustomHint, в том числе, как и стандартный THint, так и TBalloonHint (или вообще написать собственный control подсказки).

TButton обзавёлся свойством Style, которое даёт ему возможность выборочного поведения. Стиль по умолчанию – bsNormal. Стиль bsCommandLink воспроизводит стиль Windows Vista, см. рисунок ниже. Этот стиль отличается от стиля bsNormal специфическим видом, иконкой и подсказкой с мелким текстом под основным Caption`ом. При использовании под ОС Windows XP и ниже его вид идентичен bsNormal. Стиль bsSplitButton добавляет к кнопке выпадающее меню по щелчку по ней. Оба стиля занимают центр и низ второго скриншота.


Рисунок 1.

Кроме того, TButton обзавёлся картинкой (см. верхнюю часть рисунка 2). TBitBtn остаётся только для обратной совместимости.


Рисунок 2.

У TEdit`a имеется свойство NumbersOnly, позволяющее вводить туда только цифры. Предупреждающее об ошибке сообщение показано в верхней части рисунка 3. Вообще говоря, свойство добавлено только потому, что таковая возможность появилась в API Windows Vista, однако всё равно несуразность нововведения бросается в глаза: неужели нельзя было соорудить собственное решение на основе регулярных выражений?

Ещё TEdit`у добавили свойство TextHint. Это серый текст подсказки, который отображается в поле ввода только когда оно неактивно. Удобно во всех отношениях.

Старое свойство TEdit`а PasswordChar, будучи установленным в «*», теперь подстраивается под тему Windows, и отображает её символ маскировки. Чаще всего это знакомый заполненный чёрный кружок.

Новый TProgressBar (см. рисунок 4) тоже меняет свой стиль. Он может оставаться нормальным, в таком случае на Vista к нему добавляется анимация, может быть поставлен на паузу (визуально: остановка, плюс на Vista замирает анимация), может указать на ошибку выполнения (цвет бара меняется на красный), а может вообще изменить стиль на Marquee – знакомую нам бегающую из стороны в сторону полосу.


Рисунок 4.

Новое свойство TListView и TTreeView – коллекция Groups. Позволяет группировать элементы списка, давая каждой из групп заголовок, подпись и иконку, как показано на рисунке 5.


Рисунок 5.

Материалы для дополнительного чтения:

TImageList

TImageList теперь может содержать в себе любые изображения, которые поддерживаются TImage. А поскольку замечательный и знаменитый проект TPNGImage в июле 2008 года перешёл под эгиду CodeGear, то теперь формат PNG изначально поддерживается во всех частях VCL.

Более подробно можно узнать об этом по по адресу http://blogs.codegear.com/nickhodges/2008/08/13/39100.

Ribbon-интерфейс

Новое слово в Visual Component Library – это, конечно же, Ribbon. Да, теперь Delphi включает в себя полную поддержку создания интерфейсов в стиле Microsoft Office 2007, со всеми его возможностями. Более того, вместе с Delphi будет традиционно поставляться демо-приложение, в котором ребята из CodeGear, не мудрствуя лукаво, воссоздали половину Word 2007.

Следует предупредить: чтобы разрабатывать приложения в Ribbon-стиле, вы обязаны получить у Microsoft лицензию (http://msdn.microsoft.com/ru-ru/office/aa973809(en-us).aspx). Лицензия бесплатная, ничего особенного собою не представляет, просто Microsoft настолько «is proud of the results of our massive investment in usability and innovative user interface design», что не могут позволить пользоваться этой идеей всем без разрешения. Процесс получения лицензии представляет собою не более чем десять щелчков мышкой и никаких затруднений не вызывает.

Более подробно можно узнать об этом в статье Delphi 2009 (beta): Ribbon Controls (http://www.flickdotnet.de/index.php/2008/08/delphi-2009-beta-ribbon-controls/).

IDE

А теперь про новые мастера и усовершенствования в IDE. Первое окошко называется Class Explorer, оно позволяет легко передвигаться по иерархии классов в вашем проекте, переходить к объявлениям и реализациям полей, свойств и методов, находить для классов предков и потомков, а также отображает переменные тех классов, которые, собственно говоря, задействованы в Class Explorer. Кроме того, разумеется, в Class Explorer наличествуют специальные диалоги для добавления полей, свойств и методов. Мне лично чем-то это всё напоминает CnWizards, GExperts... в общем, всю ту гигантскую компанию коллекций экспертов от третьих лиц, которыми мы уже который год постоянно расширяем Delphi. Да, до их функциональности CodeGear RAD Studio пока далеко, но лучше так, чем превращаться в тяжёлого и неповоротливого мультифункционального монстра. Так что просто порадуемся старому знакомому окошечку.

Второй мастер – из разряда "наконец-то дождались". Это Resource Manager, позволяющий добавлять в проект Windows-ресурсы, со всей необходимой функциональностью. Теперь, по крайней мере, больше не нужно совершать при сборке множество дополнительных манипуляций с res-файлами и Restorator`ом. Ничего особенного, в принципе, от Resource Manager-a, ждать не стоит: добавление ресурса, указание его типа и идентификатора – а больше от ресурсов и не требуется.

Третье нововведение в IDE носит название "build configurations and options sets". Это нововведение позволяет создавать подборки параметров для сборки проекта, таких, как имеющиеся по умолчанию Debug и Release. Теперь такие спецподборки можно создавать в неограниченном количестве, присваивать им имена, а также импортировать и экспортировать в собственном формате CodeGear – "option set". Случаев, когда подобное может пригодиться, особенно в специфических условиях постоянной отладки, тестирования и профилирования, – не счесть.

Два слова об отладчике. В Windows Vista появилась замечательная возможность под именем "Wait Chain" – "очередь ожидания". Она позволяет отслеживать иерархию потоков, где некоторые остановились в определённых точках и ожидают сигналов от других. Таким образом, когда в многопоточном приложении один поток ожидает сигнала от другого, приостановив свое выполнение, вы будете видеть всё дерево в отладчике как Wait Chain. Разумеется, с указанием состояния, статуса, ID и ассемблерного кода. Согласно MSDN, “WCT (Wait Chain Traversal) currently supports the following synchronization primitives: ALPC, COM, critical sections, mutexes, SendMessage and wait operations on processes and threads”.

Небольшой новый (вернее, хорошо забытый старый) мастер "The ActiveX Control Wizard" проводит нас сквозь процесс создания VCL-компонента из ActiveX-контролов.

Неудобный фильтр в панели компонентов заменён на нововведённый TButtonedEdit. Собственно говоря, если быть до конца откровенным, то эту строку поиска в лучших традициях Vista внедрили всюду, куда она только подходила: в половину диалогов, в часть мастеров... почти везде, где присутствуют какие-либо списки.

Почти все диалоги теперь растягиваются и гораздо удобнее организованы.

Материалы для дополнительного чтения

Справка

Брюс МакГи пишет, что CodeGear повысила удобство справки Delphi, в частности добавила туда отдельную классификацию для защищённых полей, событий, методов и свойств.

Разное

Этот раздел – подборка разнообразных фактов о Delphi (только о Delphi), которые автору не удалось категоризировать.

Уменьшение требований

Инсталлятор Tiburon-a теперь не требует наличия предустановленного на компьютере Microsoft .NET SDK, ему достаточно установить для просмотра справки последнюю версию Microsoft Document Explorer. Таким образом, так называемый prerequisite-список сократился на один пункт.

Скорость установки

Процесс установки ускорился, по меньшей мере, вдвое. Таким образом, если установка CodeGear RAD Studio 2007 Highlander занимала на среднестатистической машине от 10 до 30 минут, то Дэвиду Интерсаймону для инсталляции Tiburon'a потребовалось приблизительно 10 минут, а Холгеру Флику вообще ровно 5.

DataSnap

Колоссальный прогресс сделан также в технологии DataSnap (она же dbExpress Remoting). Она теперь не зависит от СОМ, поддерживает вызов серверных методов, как если бы они были написаны на стороне клиента, и многое другое.

Более подробно можно узнать об этом по ссылкам:

TObject в стиле .NET

TObject унаследовал три полезные мелочи от своего дотнетовского собрата. Это функции:

Еще три удобных функции, не позаимствованные из .NET – MethodAddress, FieldAddress, UnitName – получили поддержку Unicode.

class function MethodAddress(const Name: string): Pointer; overload;
function FieldAddress(const Name: string): Pointer; overload;
class function UnitName: string;
function Equals(Obj: TObject): Boolean; virtual;
function GetHashCode: Integer; virtual;
function ToString: string; virtual;

Новая конструкция Exit

Следующая возможность действительно относится к разряду давно ожидаемых. Многим надоедало постоянно в функциях писать две отдельные строчки для выхода из середины тела функции: сначала присваивать значение результату, а потом уж выходить. Да, использование результата как отдельной неявной переменной Result – очень удачная техническая находка, она просто незаменима во многих выражениях... но всё-таки в данном конкретном случае это неудобно. Теперь CodeGear ввела в Delphi конструкцию, сходную с C-оператором return. На самом-то деле они всего лишь добавили один параметр в функцию Exit(): параметр, которым надо передать ей возвращаемое значение функции при немедленном выходе. Параметр этот не имеет определённого типа, тип его определяется в момент компиляции и становится равным типу результата функции. Вот две функции для сравнения:

function DoSomething(aInteger: integer): string;
begin
  if aInteger < 0 then
  begin
    Exit('Negative');
  end;
  Result := 'Positive';
end;

function DoSomething(aInteger: integer): string;
begin
  if aInteger < 0 then
  begin
    Result := 'Negative';
    Exit;
  end;
  Result := 'Positive';
end;

Они идентичны с точки зрения компилятора Delphi 2009.

Арифметика указателей

Новая директива {$POINTERMATH} позволяет работать с указателями в стиле C. То есть типизированный указатель учитывает размерность типа, и арифметические операции над таким указателем будут сдвигать его на количество байт, пропорциональное размеру типа. Для произвольного сдвига теперь не требуется никакого конвертирования:

var
  p: pInteger;
  offset: integer;
begin
  p ? $25A0;
  offset ? 1;
  {$POINTERMATH ON}
  Inc(p, offset);   
  {$POINTERMATH OFF}
  // теперь p = $25A4, так как SizeOf(Integer) = 4
end;

Псевдонимы целочисленных типов

В System.pas теперь можно найти следующий полезный набор псевдонимов:

type
  Int8   = ShortInt;
  Int16  = SmallInt;
  Int32  = Integer;
  UInt8  = Byte;
  UInt16 = Word;
  UInt32 = Cardinal;

Также мы наконец-то дождались появления типа UInt64 – беззнакового 64-битного целого, аналога qword во Free Pascal и uint64_t (unsigned long long) в C/C++.

Книга Кэнту

Марко Кэнту в настоящее время пишет книгу, которая посвящена только и исключительно Tiburon – то есть всем нововведениям в этой версии. Учитывая, что нововведений уйма, Марко даже и не надеется успеть с книгой к выпуску. Что ж, будем надеяться, что, по крайней мере, через месяц этот достойный труд будет готов. Жаль только, что читать придётся на английском: перевод у нас подобной книги раньше, чем годика через два – нечто из ряда вон выходящее.

ER\Studio

Как Delphi, так и Builder доступны в трёх вариантах: привычные для нас Proffessional и Enterprise, а также под лицензией Architect, включающей в поставку Embarcadero ER\Studio Developer Edition. Очевидцы говорят, что для людей, у которых много работы связано с базами данных, это просто незаменимый по возможностям продукт. Я оставляю оценку специалистам и читателям, а сам только предоставляю ссылки на дополнительные сведения и немного демонстрационного видео:

Вебинары

Весь август и сентябрь CodeGear проводили вебинары, на которых подробно и обстоятельно было рассказано обо всех новых тонкостях Тибурона. Каждый из вебинаров был записан, обработан, и выложен на сайте компании для свободной загрузки и просмотра. Ссылка прилагается:

C++Builder

Касательно второй половины долгожданного Тибурона, к сожалению, сказать получится гораздо меньше. Информации немного, господа из CodeGear или предпочитают не распространяться по поводу C++Builder, или упоминать, что его развитие полностью аналогично Delphi (что, в принципе, верно: к примеру, параллельно со своим собратом C++Builder приобретает отныне тип System?UnicodeString и все сопутствующие ему новинки), либо повторяют одни и те же рассказы в разных формулировках и с разной степенью подробности.

Повторюсь, большинство фактов, описанных в предыдущих разделах, имеют место и здесь. Про Unicode уже сказано. Поскольку Delphi и C++Builder используют одну и ту же оболочку, то все факты раздела «IDE и VCL» в равной степени относятся и к C++Builder также. Здесь же я попытаюсь разобрать только специфические нововведения этой среды и языка.

C++0x

Первое и самое значительное изменение касается C++0x. Что такое С++0x? Это планируемый новый стандарт языка С++ (http://en.wikipedia.org/wiki/C%2B%2B0x). Он ещё полностью не выработан и тем более не утверждён, однако определённый набор новых элементов языка уже стопроцентно будет в нём присутствовать. На самом деле C++0x – это гигантское расширение языка. Утверждается, что в С++ будет внедрено около 50 новых языковых конструкций разной степени значимости, которые позволят ему:

CodeGear заявляет о ранней поддержке (early support) С++0x в C++Builder 2009. Под этим имеется в виду поддержка некоторого набора пунктов будущего стандарта. И по некоторым (к сожалению, не по всем) из них я сейчас вкратце пройдусь.

Decltype

Функция автоматического описания типа переменной или выражения (в том числе в шаблонах).

Переменные, заключённые в decltype(), считаются ссылками до тех пор, пока не будут обёрнуты в скобки. Пример:

decltype(container.begin()) i = container.begin(); // простое использование decltype() для определения типа
int a;
decltype(a) b; // b есть int &
decltype((a)) c; // c есть int

Метафункции

Метафункции – это шаблоны, которые используются для описания свойств у типов, зачастую неизвестных на момент написания. Выглядят как is_class<T>, is_reference<T> и будут продемонстрированы в примере ниже.

[[final]]

Атрибут, аналогичный ключевому слову final в Delphi и Java, sealed в C#. Виртуальная функция, помеченная этим атрибутом, не может быть перегружена в классах-наследниках. Класс, помеченный таким атрибутом, не может иметь наследников (соответственно, все его функции-члены автоматически также становятся [[final]]). Существует термин для данного явления: запечатывание класса/метода. Если вы стопроцентно знаете, что в данном проекте классы/функции перегружаться и наследоваться пока что не будут, можно смело запечатывать их – это существенно ускорит программу, потому что будут использоваться только статические вызовы.

[[noreturn]]

Атрибут скорее для программиста, чем для компилятора. [[noreturn]]-функция не возвращает никакого значения: то есть, гарантированно возбуждает исключение. Если компилятор обнаружит, что функция, помеченная как [[noreturn]], имеет хотя бы одну корректную точку выхода, он уведомит об этом сообщением об ошибке.

Новое лицо enum

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

enum Days : char;  // Допускается предекларация с указанием базового типа
void ProcessDay(Days d);

enum class Months 
{  // базовый тип неявно int
  January,  February,  March,
  April,    May,       June,
  July,     August,    September,
  October,  November,  December
};

enum Days : char 
{
  Sunday,   Monday,
  Tuesday,  Wednesday,
  Thursday, Friday,
  Saturday
};

void ProcessDay (Days d) 
{
  if (d == Days::Monday)  // уточнение допускается...
  {
    // код для понедельника
  }
  else if (d == Tuesday)  // ...но не требуется
  {
    ...
  }
}

void ProcessMonth (Months m) 
{
  if (m == Months::January)
  {
    // код для января
  }
  else if (m == February) // Ошибка: enum должен быть уточнён
  {
    ...
  }
}

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

Далее, как сказано в комментариях, можно указать, какой тип будет базовым для данного enum`a; иначе говоря, какой тип будет использоваться для хранения значений этого перечисления. В коде базовым типом для enum`a Days сделан char, а для enum`a Months оставлен устанавливаемый по умолчанию int.

Следующими по списку идут ключевые слова class enum. Сделано это в первую очередь для того, чтобы избежать конфликта имён: при использовании члена такого перечисления он должен быть квалифицирован именем типа (т.е. Months::January – правильно, а January – нет). Сравните две функции в коде. В первой к константе можно обращаться двумя способами, но во второй использование константы без конкретизации enum`a вызовет ошибку компиляции.

Статические проверки, выравнивание

Взгляните на незнакомые выражения в нижеприведённом исходном коде:

#include <type_traits>
using namespace std;

static_assert(is_signed<char>::value, "Ожидаются символы со знаком");

struct POD
{
    char c;
    double d;
};

static_assert (sizeof(POD) == 16, "Требуется четырёхбайтное выравнивание");
// static_assert (sizeof(POD) == 9, "Требуется однобайтное выравнивание");

template<typename T>
void Polymorphic(T* t)
{
  static_assert(is_polymorphic<T>::value, "Тип должен быть полиморфным");

  // можно смело удалять,
  // dynamic_cast сработает корректно,
  // и т.д.
  delete t;
};

Это статические проверки (static assertions). В отличие от обычных проверок, статические выполняются в момент компиляции. Так, они могут проверять некоторые условия компиляции, характеристики системы, использование типов и много чего другого – главное, чтобы оно было доступно на момент компиляции. В коде есть 3 статические проверки: первая проверяет, включёна ли опция "Символы со знаком", вторая – что выравнивание данных структуры POD четырёхбайтное (или однобайтное, если использовать закомментированный вариант). Последняя проверяет, что тип «T» должен быть полиморфным.

UML-моделирование

UML-моделирование, бывшее доселе исключительно прерогативой Delphi-разработчиков, теперь добралось и до C++ Builder. Решение получилось действительно функциональным, в отличие от C++ Builder 2007, где от диаграмм никакого толка не было. C++Builder 2009 же дает программисту возможности интерактивного редактирования: любые изменения в UML немедленно отражаются в коде, и наоборот. Можно работать как с тем, так и с другим по факту, и это даст одинаковый результат... разве что в одних случаях удобнее диаграммы, а в других – код программы.

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

Еще одно преимущество представления в виде диаграммы – оно позволяет легко и быстро генерировать по проекту HTML-документацию.

Библиотеки

Одно из самых занимательных новшеств: отныне библиотека boost (на данный момент версия 1.35) поставляется вместе с C++Builder. Это существенно расширяет его возможности как по сравнению с C++Builder 2007, так и по сравнению с Delphi. Да, и раньше boost можно было спокойно подключить и использовать, однако теперь это уже изначально сделано средой – а значит, любые возможности boost (графы, многопоточность, регулярные выражения, юнит-тестирование, работу с памятью, «умные указатели», линейную алгебру, генераторы случайных чисел, вариантные/неопределённые типы данных...) можно использовать без всяких сложностей. Заголовочные файлы, как им и положено, лежат в папке include.

Второй поставляемой отныне с C++Builder библиотекой стал Technical Report 1 – TR1 (также являющийся частью будущего стандарта C++0х – прим.ред.). Это расширение стандартной библиотеки, направленное на глубокую поддержку разнообразных математических действий, улучшение функциональности контейнеров (равно как и добавление совершенно новых, к примеру, хешированных) и ещё один собственный вариант генераторов случайных чисел. А также, разумеется, множество прочих приятных мелочей.

Материалы для дополнительного чтения

Заключение

Общее количество направлений, которые открывает перед нами CodeGear c этим продуктом, не может не впечатлять. В самом деле, мы оказались сразу на несколько ступенек выше того уровня, на котором находились языки и среда в 2007 году, и достигли (а в некоторых сферах – и превзошли) многие признанные мировые стандарты программирования. Создаётся впечатление, что разработчики ставили перед собою цель одним ударом развеять сразу несколько древних мифов о Delphi и C++Builder, которые бродили по программистскому миру и создавали любимым инструментам зачастую не лучшую репутацию. И это у них блестяще получилось.

Теперь многое в прошлом. Теперь Delphi-программист может смело ориентироваться на всю планету, используя в своих проектах многоязычность и Unicode; C++-программист может заглянуть в будущее и с удобством воспользоваться преимуществами только готовящегося стандарта; и наконец, они оба могут пожать друг другу руки и использовать в своих наработках код, написанный собратом на другом языке – но с использованием аналогичных технологий. Отныне это просто.

В сообществе было много волнений, толков и раздумий по поводу продажи CodeGear Borland`ом корпорации Embarcadero. Обсуждались самые различные варианты исхода событий, было много пессимистов и разочаровавшихся личностей. Но теперь все скептики посрамлены: новый хозяин доказал свою заботу о продукте. Embarcadero ориентируется на ПО, связанное с базами данных – мы получили тесную интеграцию среды с ER\Studio и существенно развитый DataSnap. Embarcadero беспокоится о мировом распространении своих продуктов – готова полная поддержка Unicode и перевод сайта CodeGear.com на русский (в том числе многих материалов). Embarcadero – разработчик кроссплатформенных решений... кто знает, кто знает, чего ещё нам ждать через год, помимо 64-битной поддержки? Возможно, такой же долгожданный кросс-компилятор под UNIX-системы?


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

Copyright © 1994-2016 ООО "К-Пресс"