Написание модулей обработки данных, не зависящих от их типа (FPC)
   
  для FPC 2.2.0 и выше - Generic-и
   

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

Рассмотрим, например, случай, описанный здесь: работа с типом запись...

Допустим, мы имеем тот же фрагмент модуля:

Type
  TType = Integer;
  TArray = Object
    ...
    Function max: TType;
    Procedure PrintAll;
    ...
  End;
  
Function TArray.max: TType;
Var maxValue: TType; i: Word;
Begin
  maxValue := arr^[1];
  For i := 2 To SizeOfArray Do Begin
    If arr^[i] > maxValue
    Then maxValue := arr^[i];
  End;
  max := maxValue
End;

Procedure TArray.PrintAll;
Begin
  For i := 1 To SizeOfArray Do
    Write(arr^[i], ' ' );
  End;
, и хотим добавить работу с типом "Запись":
Type
  TPoint = Record
    X, Y: Integer;
  End;
Что для этого придется сделать в FPC? Чтобы ответить на этот вопрос, нужно сначала выяснить, что же препятствует использованию массива TArray совместно с типом TPoint. Давайте посмотрим на приведенный для компилятора TP список проблем:

  1. Функции в Паскале не могут возвращать результат типа Record.
    Free Pascal разрешает функции возвращать результат типа "Запись"
  2. В функции нахождения макс. элемента используется сравнение элементов, но для Record-ов эта операция не определена.
    Это все так же не позволяет подставить TPoint в наш модуль, но решается эта проблема без изменения тела функции, добавлением перегрузки оператора сравнения (напомню, что для этого режим совместимости должен быть установлен в ObjFPC):
    operator > (const A, B: TPoint) R: Boolean;
    begin
      R := (A.X > B.X);
    end;
  3. При выводе всего массива на экран используется процедура Write, которая не умеет работать с типами, определенными пользователем.
    Компилятор FPC позволяет перегружать процедуры/функции, так что эта проблема также решается простым определением процедуры Write для типа TPoint:
    procedure write(const R: TPoint);
    begin
      system.write('(X:', R.X, ' Y:', R.Y, ')');
    end;
    Для того, чтобы метод PrintAll корректно работал с любым типом данных, его придется немного подкорректировать:
    procedure TArray.PrintAll;
    var i: integer;
    begin
      for i := 1 to SizeOfArray do begin
        Write(arr^[i]); system.Write(' ');
      end;
    end;
Теперь посмотрим, что же нужно изменить в модуле, чтобы он стал работать с типом TPoint.

Если для типа Integer модуль-"обертка" имеет вид:
Unit Arr_Int;
interface

type
  TType = Integer;
{$i ar_inter.scb}

type
  TArrayInt = TArray;
  
implementation

{$i ar_implm.scb}
end.

то для работы с типом TPoint нужно чуть-чуть его подкорректировать:
{$mode ObjFPC}
unit Arr_Rec;
interface

type
  TPoint = record
    x, y: Integer;
  end;
  TType = TPoint;
  
{$i ar_inter.scb}

type
  TArrayPoint = TArray;

implementation

{ Здесь описываем перегрузку операторов и новую процедуру Write }
operator > (const A, B: TPoint) R: Boolean;
begin
  R := (A.X > B.X);
end;

procedure write(const R: TPoint);
begin
  system.write('(X:', R.X, ' Y:', R.Y, ') ');
end;

{$i ar_implm.scb}
end.

Обратите внимание: файл ar_implm.scb остался в первозданном виде (за исключением незначительного изменения метода PrintAll), а значит, он будет более читаемым по сравнению с тем файлом ar_implm.scb, который получился для компилятора TP...



Free Web Hosting