Еще о Generic-ах (FPC)
Начало - см. здесь  
   

При более детальном рассмотрении этого нововведения в FPC оказывается, что:

  1. Generic-и не обязательно должны иметь один шаблонный параметр...

    Поскольку в описании их формата (ref.pdf, 8.2 "Generic Class Definition") сказано:
    -- generic type generic identifier < template list > = generic class ;
    , то вполне естественно будет попробовать, например, реализовать ассоциативный массив (аналог - контейнер std::map).
    type
      generic
      TMap<TK, TV> = object
        ...
      end;
    Как видим, и такая конструкция вполне работоспособна...

  2. Еще один недостаток Generic-ов в их теперешнем виде - это невозможность объявить шаблонную запись (Record).

    Хотя одно из сообщений компилятора и гласит:
    "Error: This type can't be a generic"
    (с расшифровкой: "Only classes, Objects, Interfaces and Records allowed to be used as generics"), и несмотря на то, что конструкция вида:
    type
      generic
      TRec<T> = record
        i: integer;
      end;
    успешно проходит компиляцию, однако при попытке сделать то, ради чего и нужны шаблоны (описать в записи поле типа T):
    type
      generic
      TRec<T> = record
        i: T;
      end;
    , компилятор начинает ругаться, что ему непонятен тип T.

    Тем не менее, способ объявить шаблонную запись все равно есть: нужно "обернуть" ее объектом, и описать нужный тип как локальный тип объекта (с public-доступом. Несмотря на то, что в настоящее время все локальные типы / переменные / методы Generic-классов трактуются как общие - это ошибка, и чтобы после ее исправления не было необходимости править уже написанные программы, лучше сразу сделать без ошибок).

    Пример:
    type
      generic
      TObjRec<T> = class
        type public
          TRec = record
            value: T;
          end;
      end;
      
      type
        intObj = specialize TObjRec<integer>;
      var
        my_rec: intObj.TRec;
      begin
        myRec.value := 20;
        writeln(myRec.value + 15);
      end.
      
  3. Естественно, для удобства использования конструкций с дженериками, не хватает Generic-функций. И хотя в планах разработчиков есть включение подобных функций в будущем, пользоваться ими можно уже сейчас.

    Все, что для этого понадобится - опять же... Да, именно это: "обернуть" функцию классом.
    type
      generic
      Func<T> = class
      type public
        procedure swap(var a, b: T);
        class procedure sort(var ar: array of T; n: integer); // <--- !!!
      end;
    
    function index(x: integer): integer;
    begin
      result := pred(x)
    end;
    
    procedure Func.swap(var a, b: T);
    var X: T;
    begin
      X := a; a := b; b := X
    end;
    
    class procedure Func.sort(var ar: array of T; n: integer);
    var i, j: integer;
    begin
      for i := 1 to n do
        for j := n downto i + 1 do
          if ar[index(pred(j))] > ar[index(j)] then
            swap(ar[index(pred(j))], ar[index(j)]);
    end;
    Почему понадобилось описывать sort, как class procedure? Очень просто: для того, чтобы использовать ее, не создавая экземпляра класса:
    type
      intFunc = specialize Func<integer>;
      strFunc = specialize Func<string>;
    
    var
      i: integer;
    
    const
      n = 12;
      vec: array[1 .. 12] of integer = (
        7, 6, 15, 4, 3, 2, 1, 0, 12, 15, 98, 11
      );
      s_vec: array[1 .. 12] of string = (
        '7', '6', '15', '4', '3', '2', '1', '0', '12', '15', '98', '11'
      );
    
    begin
      for i := 1 to n do write(vec[i]:4);
      writeln;
      intFunc.sort(vec, n);
      for i := 1 to n do write(vec[i]:4);
      writeln;
    
      for i := 1 to n do write(s_vec[i]:4);
      writeln;
      strFunc.sort(s_vec, n);
      for i := 1 to n do write(s_vec[i]:4);
      writeln;
    
    end.
    Как видим, и массив целых, и массив строк прекрасно отсортировались...
( ... продолжение следует ... )



Free Web Hosting