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
_______________________________________________
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply via email to