Sven Barth
2013-02-06 19:19:12 UTC
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
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