[fpc-pascal] Feature announcement: Type helpers

Sven Barth pascaldragon at googlemail.com
Wed Feb 6 10:49:36 CET 2013


Hello Free Pascal community!

I'm pleased to announce the addition of type helpers which extend the 
existing helper concept with the ability to extend primitive types.

Motivation:

With class and record helpers the possibility was created to extend 
classes and records with types without subclassing the type (which 
wouldn't be possible with records anyway). This allows to add e.g. 
methods to types in units that you can't influence or where you can't 
influence with type is instantiated (e.g. the TStrings descendant used 
in TMemo). Now it is logical to extend this also to other types 
supported by Pascal, but here more driven by the possibility to group 
methods together and have them appear to belong to the primitive type.

While this does not bring the concepts of boxing of managed 
languages/environments like Java and .NET to Pascal it does nevertheless 
look this way.

Syntax:

The declaration of type helpers looks as follows:

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

Like class and record helpers they support all visibility sections and 
you can define methods, properties and constructors. Inside methods 
declared in the helper "Self" will be of the extended type's type and 
it's value can also be changed.
Similar to record helpers class methods MUST be declared as static.

Usage:

A type helper is active if it is in scope. This means it must either 
have been declared in the same unit before the code which wants to use 
the helper or it needs to be declared in a used unit. As with class and 
type helpers only one helper for a given type can be active and thus you 
need to keep in mind the scoping rules when using helpers (e.g. the 
current unit is searched in the order implementation section then 
interface section (if the code is in the implementation section) and 
then the used units are searched from right to left and each unit from 
top to bottom).

In some cases the meaning of a type depends on the compiler settings the 
helper is compiled with. E.g. the type Integer is either a ShortInt or a 
LongInt depending on the current mode (fpc/tp vs. objfpc/delphi) and the 
type String is different depending on the switches {$H+/-} and 
{$modeswitch unicodestring}. This needs to be kept in mind when working 
with these "generic" types. Another special case is the type Extended on 
platforms that don't support that type (and thus it will be defined as 
Double).
Additionally a type declared as "NewType = type OldType" is considered a 
completly independant type as it is the case with e.g. operator 
overloads as well.

If a helper for the type is in scope you can simply invoke it's methods 
or properties like you'd do on classes or records. Let's suppose we have 
a helper with method "ToString: String" for the type LongInt in scope 
then it will look like this:

=== example begin ===

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

=== example end ===

Additionally to invoking type helpers on variables they can also be used 
on constants though special care needs to be taken that the correct type 
is used. E.g. the constant "200" will be handled as a "Byte" whereas 
"20" will be handled as "SmallInt". Also the type of string constants 
depends on the current mode switch (especially {$H+/-} and {$modeswitch 
unicodestring}) and also the content of the string. E.g. in case of 
"{$mode objfpc}{$H+}" a string containing unicode characters will be 
handled as a UnicodeString constant and thus only helpers for 
UnicodeString will apply.

For the following example let's assume the helper from the previous 
example is in scope again:

=== example begin ===

begin
   Writeln($12345678.ToString);
end.

=== example end ===

Additionally addresses (e.g. "@i") (type: Pointer) and the built in 
constants "True" (type: Boolean), "False" (type: Boolean) and "Nil" 
(type: Pointer) are allowed as well. Please note that in the case of 
typed addresses also the untyped pointer type will be used, because a 
corresponding typename might not be available to the current unit (and 
thus the helper could not be found). Constants that can't be used with 
type helpers are set and array constructors.

Class methods (and properties) can be either used on variables or 
constants, but also on the type name itself:

=== example begin ===

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.

=== example end ===

Supported types:

There is a restrictions on the type which can be extended. Basically 
every primitive type is allowed besides the following:

* file types (TextFile, file of ...)
* procedural variables

Of course types like records, classes, Objective C classes, C++ classes, 
objects and interfaces are forbidden as well.

Viewed from the other perspective this means that the following types 
are allowed:

* ordinal types
* string types (including char types)
* set and enum types
* array types
* boolean types
* Variant
* floating point types
* pointer types

Delphi compatibility:

This feature was introduced by Delphi XE3, but instead of using the more 
logical "type helper" syntax they added support for primitive types to 
the "record helper" one. Thus to keep Delphi compatibility this is also 
the syntax used in mode Delphi. Also in non Delphi modes inheritance for 
helper types is supported.

Future developments:

Add support for more types, especially interface and object types. They 
will either be added as class helpers or as "object helper" and 
"interface helper" (as objects and interfaces support inheritance the 
scoping rules inside the helper will resemble class helpers more than 
record/type helpers).

Allow that multiple helpers are active for one type at a single time. 
This is one of the main restrictions of the current helper 
implementation. For this corresponding lookup rules need to be defined 
and the feature will be added as a modeswitch (maybe by default enabled 
in mode objfpc).

Add support for helpers to the JVM target. The most likely 
implementation approach is to pass the "Self" value as an additional 
parameter and treat the methods otherwise as static ones (like is done 
in .NET for helper methods).

Regards,
Sven



More information about the fpc-pascal mailing list