Добро пожаловать!

Я Дож, программирование — моё хобби. По мере того, как я осваиваю что-то новое, стараюсь об этом написать пост.
На текущий момент в блоге затронуты следующие темы: vim, free pascal, lisp, forth, m4
Занимаюсь разработкой своего языка под названием DEmbro, подбробней: wiki и svn
Для постов, не связанных с программированием, у меня есть отдельное жж.

августа 31, 2014

Free Pascal: Type Helpers

Это мой перевод анонса так называемых type helpers, сделанного в феврале 2013 года. На мой взгляд, он достаточно хорошо и подробно описывает эту фичу. Оригинальный текст письма доступен здесь.


Приветствую сообщество Free Pascal!

Я рад представить дополнение к хелперам, которое распространяет существующую концепцию хелперов на примитивные типы.

Мотивация

С хелперами для классов и записей появилась возможность расширять классы и записи без их наследования (что всё равно было бы невозможно для записей). К примеру, это позволяет добавлять методы типам из других модулей, на которые вы не можете влиять напрямую, или не можете повлиять на экземпляр (например, используемый в TMemo наследник TStrings). Сейчас логично расширить это также и на другие типы, поддерживаемые Паскалём, но теперь нами движет в большей степени возможность сгруппировать методы вместе для примитивных типов.

Здесь не будет идти речь об использовании в паскале концепции объекта-обёртки в управляемых языках/окружениях, таких как Java и .NET, но всё это можно рассматривать и с этой точки зрения.

Синтаксис

Объявление хелперов для типов выглядит следующим образом:

TYPENAME = type helper[(BASEHELPER)] for EXTENDEDTYPE
   DECLARATIONS
end;

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

Аналогично хелперам для записей, классовые методы ДОЛЖНЫ быть объявлены статичными. (Прим. пер.: т.е. использована директива static.)

Использование

Хелперы для типов активны, если они видимы. Это означает, что либо они должны быть объявлены в том же модуле до использующего их кода, либо они должны быть объявлены в используемом модуле. Как и в случае с хелперами для классов и записей, только один типовой хелпер может быть активен для одного заданного типа и вы должны следить за видимостью, когда используете хелперы (к примеру, в текущем модуле сперва проверяется implementation секция, затем interface (если код в implementation секции) и затем используемые модули просматриваются справа налево и каждый модуль сверху вниз).

В некоторых случаях смысл типа зависит от настроек компилятора, с которыми хелпер был скомпилирован. К примеру, тип Integer может означать либо ShortInt, либо LongInt в зависимости от текущего режима (fpc/tp и objfpc/delphi соответственно) и тип String отличается в зависимости от переключателей {$H+/-} и {$modeswitch unicodestring}. Это нужно помнить при использовании их в «generic» типах. Другой специфичный случай — тип Extended на платформах, которые не поддерживают этот тип (и поэтому могут быть определены как Double). Кроме того, тип, объявленный как «NewType = type OldType», считается полностью независимым типом, к примеру в случае перегрузки операторов.

Если хелпер для типа находится в области видимости, вы можете без проблем вызывать его методы и свойства как будто вы работаете с классами или записями. Давайте предположим, что у нас есть хелпер с методом «ToString: String» для типа LongInt в области видимости, тогда выглядеть это будет как-то так:

var
    i: LongInt;
begin
    Writeln(i.ToString);
end.

Кроме вызова типовых хелперов на переменных, их можно также вызвать на константах, но нужно позаботиться о том, что будет использован корректный тип. К примеру, константа «200» будет иметь типа «Byte», тогда как «20» — тип «SmallInt». Кроме того, тип строковых констант зависит от текущего используемого режима (в особеноости от {$H+/-} и {$modeswitch unicodestring}) и содержимого строки. К примеру, в случае "{$mode objfpc}{$H+}" строка, содержащая юникодные символы, будет рассматриваться как константа типа UnicodeString и поэтому только хелперы для UnicodeString будут применены.

Для следующего примера предположим, что хелпер из предыдущего примера опять находится в области видимости:

begin
   Writeln($12345678.ToString);
end.

Также допустимы указатели (к примеру, "@i") (тип: Pointer) и встроенные константы: "True" (тип: Boolean), "False" (тип: Boolean) и "Nil" (тип: Pointer). Пожалуйста, обратите внимание, что в случае типизированных указателей будет использован нетипизированный указатель, потому что соответствующее имя типа не может быть доступно в текущем модуле (а значит, хелпер не может быть найден). Константы, которые не могут быть использованы с типовыми хелперами — множества и конструкторы массивов.

Классовые методы (и свойства) могут быть применены не только к переменным и константам, но и к самим типам:

type
   TLongIntHelper = type helper for LongInt
     class procedure Test; static;
   end;

class procedure TLongIntHelper.Test;
begin
   Writeln('Test');
end;

var
   i: LongInt;
begin
   i.Test;
   $12345678.Test;
   LongInt.Test;
end.

Поддерживаемые типы

Имеются ограничения на типы, которые могут быть расширены. Любой примитивный тип может быть расширен, за исключением:

  • файловых типов (TextFile, file of ...)
  • процедурных переменных

Конечно, также запрещены записи, классы, Objective C классы, C++ классы, объекты и интерфейсы.

Если смотреть с другой стороны, то это значит, что следующие типы разрешены:

  • порядковые типы
  • сроковые типы (включая символьные типы)
  • множества и перечислимые типы
  • массивы
  • булевские типы
  • Variant
  • типы чисел с плавающей точкой
  • указатели

Совместимость с Delphi

Эта возможность введена в Delphi XE3, но вместо использования более логичного синтаксиса «type helper» была добавлена поддержка для примитивных типов «record helper». Поэтому для поддержания совместимости с Delphi такой синтаксис также используется в Delphi режиме. Кроме того, в не Delphi режимах поддерживается наследование для типовых хелперов.

Будущее развитие

  • Добавить поддержку для большего числа типов, особенно для интерфейсов и объектов. Они могут быть добавлены либо как классовые хелперы, либо как «object helper» и «interface helper» (т.к. объекты и интерфейсы поддерживают наследование правил видимостей, внутри они напоминают хелперы для классов больше, чем хелперы для записей/типов).
  • Позволить нескольким хелперам быть активными для одного типа в одно и то же время. Это одно из основных ограничений текущей реализации хелперов. Для этого нужно продумать соответствующие правила поиска, а возможность добавить как modeswitch (быть может, по умолчанию включив её в objfpc).
  • Добавить поддержку хелперов в JVM версии. Наиболее разумным подходом в реализации этого кажется передавать переменную «Self» как дополнительный параметр и рассматривать методы как статичные (как это сделано в .NET для методов в хелперах).

С уважением,
Свен


Читать дальше......

Постоянные читатели

Обо мне

Моя фотография
Мой e-mail: vitek_03(at)mail(dot)ru