Различия между Delphi и FPC
 Добавлено: 30.06.2009 00:44:41
Добавлено: 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 указателю можно присвоить целочисленную константу...
Все, пока хватит.

