Различия между Delphi и FPC
Добавлено: 30.06.2009 00:44:41
Последний месяц занимаюсь переводом большого Delphi проекта под FPC64, цель - получить код, компилирующийся под Win32/Win64 на Delphi и FPC. Поэтому включен режим совместимости с Delphi и стараюсь, по возможности, убирать весь компиляторо-зависимый код. Неизбежно оказалось, что режим "совместимости" отнюдь не гарантирует, что программа, без ошибок работающая в Delphi будет работать и под FPC (кто бы сомневался ). Походил уже по многим граблям, вот, хочу поделиться опытом, может кому-нибудь еще будет полезно. С другой стороны буду благодарен если дополните список - с чем еще можно столкнуться.
Оговорюсь - приложение мое серверное, поэтому все что касается VCL/LCL и прочих визуальных форм не очень интересует. Интересует в первую очередь то, что компилируется, не выдает ошибок/предупреждений, но работает по разному.
Добавлено спустя 5 минут 14 секунд:
1. Порядок вычисления аргументов функции может измениться
Для Delphi последовательность 1-2-3, для FPC 2-1-3. Так что, если порядок важен - так писать не следует.
Добавлено спустя 10 минут 1 секунду:
2. Из той же области, несколько менее очевидный пример.
В Delphi результат - 2. Во FPC64 зависит от опций оптимизации - либо 1 либо 2.
Добавлено спустя 11 минут 19 секунд:
3. Иной порядок деинициализации интерфейсных переменных
В Delphi, во время вызова деструктора интерфейсная переменная уже обнулена, во FPC - еще нет. Вроде мелочь, но может сильно засадить в сложных случаях когда объекты взаимно ссылаются друг на друга через интерфейсы.
Добавлено спустя 39 минут 11 секунд:
4. А вот это уже - конкретная засада, если вы активно используете интерфейсы.
Пример несколько труден для понимания. Он демонстрирует то, как метод Proc1 вызывается на уже уничтоженом объекте (то, что в этом месте не возникает AV - чистая случайность). Видимо, FPC считает, что раз интерфейс, который вернула функция GetInterface1 ничему не присвоен, то его можно сразу уничтожить.
проблему можно обойти так:
Либо, для пущей надежности, завести локальную переменную. Вот только пойди найди все такие места
Добавлено спустя 2 минуты 59 секунд:
5. Ну, то что Set'ы, которые в Delphi имеют SizeOf = 1 или 2 байта в FPC всегда 4 байта все знают. Неприятно только, когда эти set'ы пишутся в поток...
Добавлено спустя 7 минут 13 секунд:
6. Из неприятных различий в RTL:
В FPC всегда будет генерировать одну ошибку - AV. В FPC Exception не имеет только один вариант конструктора CreateRes (и аналогичных), который получает указатель на строку. А на этапе компиляции эта ошибка не ловится, потому что (в отличии от Delphi) в FPC указателю можно присвоить целочисленную константу...
Все, пока хватит.
Оговорюсь - приложение мое серверное, поэтому все что касается VCL/LCL и прочих визуальных форм не очень интересует. Интересует в первую очередь то, что компилируется, не выдает ошибок/предупреждений, но работает по разному.
Добавлено спустя 5 минут 14 секунд:
1. Порядок вычисления аргументов функции может измениться
- Код: Выделить всё
function Func1 :String;
begin
Writeln(1);
Result := '';
end;
function Func2 :Integer;
begin
Writeln(2);
Result := 0;
end;
procedure Proc3(A :String; B :Integer);
begin
Writeln(3);
end;
begin
Proc3(Func1, Func2);
Для Delphi последовательность 1-2-3, для FPC 2-1-3. Так что, если порядок важен - так писать не следует.
Добавлено спустя 10 минут 1 секунду:
2. Из той же области, несколько менее очевидный пример.
- Код: Выделить всё
function Func1(var A :Integer) :Integer;
begin
Inc(A);
Result := 1;
end;
procedure Test1;
var
I :Integer;
begin
I := 0;
Inc(I, Func1(I));
writeln(I);
end;
В Delphi результат - 2. Во FPC64 зависит от опций оптимизации - либо 1 либо 2.
Добавлено спустя 11 минут 19 секунд:
3. Иной порядок деинициализации интерфейсных переменных
- Код: Выделить всё
var
Obj1 :IUnknown;
type
TClass1 = class(TInterfacedObject)
public
destructor Destroy; override;
end;
destructor TClass1.Destroy;
begin
Writeln(Integer(Obj1));
inherited Destroy;
end;
begin
Obj1 := TClass1.Create;
Obj1 := nil;
В Delphi, во время вызова деструктора интерфейсная переменная уже обнулена, во FPC - еще нет. Вроде мелочь, но может сильно засадить в сложных случаях когда объекты взаимно ссылаются друг на друга через интерфейсы.
Добавлено спустя 39 минут 11 секунд:
4. А вот это уже - конкретная засада, если вы активно используете интерфейсы.
- Код: Выделить всё
type
IInterface1 = interface(IUnknown)
procedure Proc1(Intf :IInterface1);
end;
TClass1 = class(TInterfacedObject, IInterface1)
public
destructor Destroy; override;
procedure Proc1(Intf :IInterface1);
end;
destructor TClass1.Destroy;
begin
writeln('Destroy: ', Integer(Self));
inherited Destroy;
end;
procedure TClass1.Proc1(Intf :IInterface1);
begin
writeln('Proc1:', Integer(Self));
end;
function GetInterface1 :IInterface1;
begin
Result := TClass1.Create;
end;
begin
GetInterface1.Proc1( GetInterface1 );
Пример несколько труден для понимания. Он демонстрирует то, как метод Proc1 вызывается на уже уничтоженом объекте (то, что в этом месте не возникает AV - чистая случайность). Видимо, FPC считает, что раз интерфейс, который вернула функция GetInterface1 ничему не присвоен, то его можно сразу уничтожить.
проблему можно обойти так:
- Код: Выделить всё
with GetInterface1 do
Proc1( GetInterface1 );
Либо, для пущей надежности, завести локальную переменную. Вот только пойди найди все такие места
Добавлено спустя 2 минуты 59 секунд:
5. Ну, то что Set'ы, которые в Delphi имеют SizeOf = 1 или 2 байта в FPC всегда 4 байта все знают. Неприятно только, когда эти set'ы пишутся в поток...
Добавлено спустя 7 минут 13 секунд:
6. Из неприятных различий в RTL:
- Код: Выделить всё
const
errSomeCodeFromResource = 123;
begin
raise Exception.CreateRes(errSomeCodeFromResource);
В FPC всегда будет генерировать одну ошибку - AV. В FPC Exception не имеет только один вариант конструктора CreateRes (и аналогичных), который получает указатель на строку. А на этапе компиляции эта ошибка не ловится, потому что (в отличии от Delphi) в FPC указателю можно присвоить целочисленную константу...
Все, пока хватит.