Saturday, September 22, 2007

DN4DP#17: .NET only: Multi-unit namespaces

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book.

The previous post covered platform differences for floating-point semantics. This time we'll look at namespace support.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Multi-unit namespaces

With the trinity of a logical unit concept, physical unit source .pas files and compiled .dcu files, Delphi has always had a very efficient and useful module concept. To address the hierarchal namespace support required in .NET while still being backwards compatible, Delphi 8 introduced the concept of dotted unit names, such as Borland.Vcl.SysUtils and Borland.Vcl.Classes - these unit names were mapped directly to .NET namespaces.

This was a step in the right direction, but Delphi 2005 extended this concept to allow multiple Delphi units to contribute to the same logical namespace. Now the namespace of a dotted unit name is everything up to the last dot.

So now both the SysUtils and Classes units reside in a single Borland.Vcl namespace. This allows the programmer to split his code into multiple physical units, while exposing the contained classes in a single logical namespace. This makes it easier and more convenient to write assemblies that can be used by other languages (such as C#).

unit MultiUnit.Namespaces.Unit2;
...
class procedure TBar2.Foo;
begin
Writeln(TBar2.ClassInfo.ToString, '.Foo');
end;

The code above writes the fully qualified namespace of the TBar2 type, and the output is MultiUnit.Namespaces.TBar2.Foo in this case.

unit MultiUnit.Namespaces.Unit1;

interface

type
TBar1 = class
class procedure Foo; static;
end;

procedure Test;

implementation

class procedure TBar1.Foo;
begin
{$IFDEF CLR}
Writeln(TBar1.ClassInfo.ToString, '.Foo');
{$ELSE}
Writeln(TBar1.ClassName, '.Foo');
{$ENDIF}
end;

procedure Test;
begin
TBar1.Foo;
end;

end.
unit MultiUnit.Namespaces.Unit2;

interface

type
TBar2 = class
class procedure Foo; static;
end;

implementation

class procedure TBar2.Foo;
begin
Writeln(TBar2.ClassInfo.ToString, '.Foo');
end;

end.
unit MultiUnit.Namespaces.Unit3;

interface

procedure Test;

implementation

uses
MultiUnit.Namespaces.Unit1,
MultiUnit.Namespaces.Unit2;

procedure Test;
begin
Writeln('Note that even though TBar1 and TBar2 are in separate Delphi units');
Writeln('they reside in the same namespace (MultiUnit.Namespaces)');
TBar1.Foo;
TBar2.Foo;
end;

end.

"

Friday, September 21, 2007

DN4DP#16: .NET only: Floating-point semantics

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book.

The previous post covered support for .NET Attributes. This time we will look at platform differences when it comes to floating-point operations.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Floating-point semantics

Native Delphi signals invalid floating-point operations by raising exceptions such as EZeroDivide and EOverflow. These semantics are preserved in Delphi for .NET by default. While the Win32 floating-point exception support is efficiently implemented directly in the processor hardware, the corresponding .NET support must be implemented explicitly in software by emitting the ckfinite IL instruction.

The .NET solution is a little slower, so if you have time-critical code that do not depend on exceptions being raised, you can speed it up a little by using the {$FINITEFLOAT OFF} compiler directive. In this mode, invalid operations will return special floating-point values like NaN (Not a Number), +Inf and -Inf (Infinity) instead of raising exceptions. To get the same semantics in Win32 code, you use the SetExceptionMask function from the Math unit.

{$IFDEF CLR}
{$FINITEFLOAT OFF}
{$ELSE}
Math.SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);
{$ENDIF}
One := 0;
Two := 42;
Three := Two / One; // Returns +Inf, no exception raised

In .NET the generated IL code for the division statement is:

FloatingPointSemanticsU.pas.51: Three := Two / One; 
IL_0063: ldloc.1
IL_0064: ldloc.0
IL_0065: div
IL_0066: ckfinite
IL_0067: stloc.2

At run time the ckfinite IL instruction actually expands into seven x86 instructions, including CALLing a routine – doing this for every floating-point operation slows things down noticeably.


Tip: Unless you absolutely need floating-point exceptions, turn them off with {$FINITEFLOAT OFF}

unit FloatingPointSemanticsU;

interface

procedure Test;

implementation

uses
SysUtils, Math;

procedure TestNaNs;
var
One, Two, Three: Double;
begin
Writeln('Floating point exceptions off:');
{$IFDEF CLR}
{$FINITEFLOAT OFF}
{$ELSE}
Math.SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide,
exOverflow, exUnderflow, exPrecision]);
{$ENDIF}
One := 0;
Two := 42;
Three := Two / One; // Returns +Inf, no exception raised
Writeln(Two:1:1 , ' / ', One:1:1, ' = ', Three:1:1);
One := 0;
Two := -42;
Three := Two / One; // Returns -Inf, no exception raised
Writeln(Two:1:1 , ' / ', One:1:1, ' = ', Three:1:1);
One := 0;
Two := 0;
Three := Two / One; // Returns NaN, no exception raised
Writeln(Two:1:1 , ' / ', One:1:1, ' = ', Three:1:1);
end;

procedure TestExceptions;
var
One, Two, Three: Double;
begin
Writeln('Floating point exceptions on :');
{$IFDEF CLR}
{$FINITEFLOAT ON}
{$ELSE}
Math.SetExceptionMask([exDenormalized, exOverflow, exUnderflow, exPrecision]);
{$ENDIF}
One := 0;
Two := 42;
try
Write(Two:1:1 , ' / ', One:1:1, ' = ');
Three := Two / One;
Writeln(Three:1:1);
except
on E:EMathError do
writeln(E.ClassName, ': ', E.Message);
end;
One := 0;
Two := -42;
try
Write(Two:1:1 , ' / ', One:1:1, ' = ');
Three := Two / One;
Writeln(Three:1:1);
except
on E:EMathError do
writeln(E.ClassName, ': ', E.Message);
end;
One := 0;
Two := 0;
try
Write(Two:1:1 , ' / ', One:1:1, ' = ');
Three := Two / One;
Writeln(Three:1:1);
except
on E:EMathError do
writeln(E.ClassName, ': ', E.Message);
end;
end;

procedure Test;
begin
TestNaNs;
writeln;
TestExceptions;
end;

end.

"

Thursday, September 20, 2007

DN4DP#15: .NET only: Attributes support

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book.

The previous post covered support for .NET Boxing operations. We continue with the .NET specific topic of Attributes support.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Attributes support

Any proper .NET language must support associating attributes to classes and members, and Delphi is no exception. The attribute syntax closely resembles C#. The attribute class name (with or without the Attribute suffix) is placed inside square brackets prior to the element you want to apply it to. The attribute name may be followed by parens with constructor parameters and property assignments. Normally, the compiler will figure out what element the attribute belongs to by its context, but you can prefix the attribute name with a target indicator and colon, such as assembly:.

type
[MyCustom(Age=42, Name='Frank Borland')]
TMyObject = class

[MyCustomAttribute('Ida Emilie', 10)]
procedure Foo;
end;

[assembly:MyCustomAttribute('Thea Ulrikke', 3)]

Sample code

unit AttributesSupportU;

interface

type
[AttributeUsage(AttributeTargets.All)]
MyCustomAttribute = class(Attribute)
public
Name: string;
Age: Integer;
constructor Create; overload;
constructor Create(AName: string); overload;
constructor Create(AName: string; AAge: integer); overload;
function ToString: string; override;
end;
type
[MyCustom(Age=42, Name='Frank Borland')]
TMyObject = class
[MyCustomAttribute('Ida Emilie', 10)]
procedure Foo;
end;

[assembly:MyCustomAttribute('Thea Ulrikke', 3)]
procedure Test;

implementation

uses
SysUtils,
System.Reflection;

{ TMyCustomAttribute }

constructor MyCustomAttribute.Create;
begin
Create('', 0);
end;

constructor MyCustomAttribute.Create(AName: string);
begin
Create(AName, 0);
end;

constructor MyCustomAttribute.Create(AName: string; AAge: integer);
begin
inherited Create;
Name := AName;
Age := AAge;
end;

function MyCustomAttribute.ToString: string;
begin
Result := Format('%s, Name=%s, Age=%d', [inherited ToString, Name, Age]);
end;

{ TMyObject }

procedure TMyObject.Foo;
begin
Writeln('TMyObject.Foo');
end;

procedure Test;
var
T: System.Type;
O: TObject;
M: MemberInfo;
A: Assembly;
begin
T := TMyObject.ClassInfo;
Writeln(T.ToString);
for O in T.GetCustomAttributes(False) do
writeln(' ', O.ToString);
Writeln;
M := T.GetMethod('Foo');
Writeln(M.ToString);
for O in M.GetCustomAttributes(False) do
writeln(' ', O.ToString);
Writeln;
A := T.Assembly;
Writeln(A.ToString);
for O in A.GetCustomAttributes(False) do
writeln(' ', O.ToString);
end;

end.

"

Tuesday, September 18, 2007

DN4DP#14: .NET platform support: Boxing

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book.

The previous post covered overloaded default array properties. The next few posts will be .NET specific - starting out with support for Boxing operations.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

".NET platform support

Custom features for a managed environment

Most of the language extensions we have discussed so far have been general in nature and applicable to both the .NET and Win32 platforms. In addition, Borland [now CodeGear - Ed.] has added new language features that map directly to intrinsics of the .NET platform. These make it easier for you to integrate with the FCL, external assemblies, the CLR and so on.

Boxing

As you know from Chapter 2 ["Just In Time Compilation" - Ed.], boxing is the process of converting a value type instance into a System.Object compatible reference, copying the value to the garbage-collected heap. By default in Delphi, boxing is an explicit operation involving a cast expression from the value type to System.Object (or the alias TObject).

While explicit boxing helps you identify spots where potentially expensive copy operations are going on, it can become bothersome in the long run. To enable implicit boxing operations (just like in C#), you can use the {$AUTOBOX ON} compiler directive.

Finally, the syntax to perform unboxing is identical with C# - just cast the TObject reference to the required value type. You can use the is operator to verify the actual value type held by the object reference.

procedure Test;
var
Collection: ArrayList;
O: TObject;
begin
Collection := ArrayList.Create;

{$AUTOBOX OFF}
Collection.Add(TObject(42));

{$AUTOBOX ON}
Collection.Add(84);

for O in Collection do
writeln(Byte(O));
end;


Note: When boxing integral constants, the compiler will box them as the smallest integral type that can hold it. If you want to box a constant as a specific type, you need to cast it or store it in a correctly typed variable first, like this Collection.Add(Integer(84)).

Monday, September 17, 2007

DN4DP#13: Overloaded default array properties

It's been a while now, but this post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book.

What a good time to continue this series just after the major Delphi for .NET 2.0 launch, CodeGear RAD Studio 2007!

The previous post covered the exotic topic of Record Helpers. Today we'll look at another exotic-ish topic - overloaded default array properties.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Overloaded default array properties

A class or component can have an array property that is declared as default. This mechanism has now been extended to allow multiple overloaded default array properties - as long as the number or types of indexer parameters are different.

type
TMyObject = class
public
property Items[Index: integer]: string read GetItems write SetItems; default;
property Items[const Name: string]: string read GetNamedItems write SetNamedItems; default;
end;

This means that you can use the array indexing syntax on the object instance - effectively overloading the array subscript operator [].

procedure Test; 
var
MyObject: TMyObject;
begin
MyObject := TMyObject.Create;
MyObject[42] := 'The Answer';
MyObject['Bar'] := 'Yes';
end;

"

Friday, September 07, 2007

RAD Studio 2007 launch in Oslo, Friday Sept 14th

The largest Delphi meetup.com group in the world (121 members and counting!), Oslo Delphi Club,  is arranging a launch event for the new Delphi and C++Builder version called RAD Studio 2007. This is the version that, amongst other things,  introduces full support for .NET 2.0 and Generics in Delphi for .NET (as shown here).

If you're in or close to Oslo, Norway make sure you're not missing this event! If you are (or want to become) a ODC member, RSVP here. You should also sign up for the event itself here.

Thanks to the new Oslo Delphi Club organizer, Kjetil Korsveien of Alfacode (the CodeGear product distributor in Norway) for organizing and hosting this event!

See you there!

Tuesday, August 28, 2007

Highlander2 Beta: Generics in Delphi for .NET

CodeGear's Delphi Product Manager Nick Hodges has once again released me from an NDA - now for the beta testing of what has been dubbed Highlander2 - the version of RAD Studio 2007 (renamed from BDS 2007) that includes an updated Delphi for .NET personality, compatible with .NET 2.0.

New stuff

There are many new features in this version, including:

  • Delphi for .NET now targets .NET 2.0, including full support for Generics (consume and create generic types)
  • ASP.NET 2.0 support, CSS templates, etc
  • ECO for VCL for .NET
  • Vista support in VCL for .NET
  • Blackfish, a managed .NET SQL database, supporting writing stored procedures and triggers in any .NET language. [I'm told there is also a Blackfish version for Java]. This is previously known as NDataStore, IIRC.
  • Updated and improved help
  • Bugfixes for Delphi Win32 and C++Builder

The Delphi Win32 personality receives a number of bug fixes (which will also be included in a free Update 3, I hear), but AFAIK, no significant new features per se. Of course, the IDE enhancements and fixes also benefit Win32 programming.

Since I'm a low-level kind of guy and a self-declared language freak, I'll mostly concentrate on the new language features in Delphi for .NET - particularly Generics.

Generics

Unless you have been living under a rock for the last few years, you'll know that Generics is the ability of programming with types as parameters at compile-time (and, in the case of .NET, at JIT-time). C++ has had templates since 1990 - which is a less constrained (literally), but more complex and potentially more powerful mechanism.

The Delphi for .NET Generics model maps directly to the CLR generics architecture that C# also uses. It is written in the stars (i.e. Delphi roadmap) that generics will also become available for Delphi Win32 in the future - it remains to be seen if this will be a superset or a subset of the current .NET implementation.

Enough talk already, lets look at some generics code in Delphi syntax.

Generic classes

Probably the most common use of generics is to define a class that has generic type parameters. These parameters are provided when a consumer uses the class - in effect instanciating a new concrete class just by declaring it. You can name type parameters anything you like.

type
TGenericClass<T> = class
Field: T;
procedure Method(A: T);
function Foo: T;
property Bar: T read Foo write Method;
end;

function TGenericClass<T>.Foo: T;
begin
Result := Field;
end;

procedure TGenericClass<T>.Method(A: T);
begin
Writeln(TObject(A).ToString);
end;

As you can see in the code above, you (or more often, Code Completion) should repeat both the class name and the generic type parameter list in the implementation of each method. This is to be able to have same-named non-generic and generic classes of different number of type parameters.


To use a generic class, supply the type parameters, and treat the result class like any other class.

type
TIntegerClass = TGenericClass<integer>;

procedure Test;
var
A: TIntegerClass;
B: TGenericClass<integer>;
C: TGenericClass<string>;
begin
A := TIntegerClass.Create;
B := TIntegerClass.Create;
C := TGenericClass<string>.Create;
A.Method(42);
end;

Note that you can use generic types to define new types and inline in variable declaration and in code. Also, different type declarations that map to the same generic type instanciation are assignment compatible.


Generic methods


You can also declare one or more generic type parameters on a method signature. The compiler will generate implementations for each unique type the method is invoked with.

type
// Generic methods - of non-generic class
TFoo = class
procedure GenericMethod<T>(A: T);
function GenericFunction<T>: T;
end;

function TFoo.GenericFunction<T>: T;
begin
Result := Default(T)
end;

procedure TFoo.GenericMethod<T>(A: T);
begin
Writeln(TObject(A).ToString);
end;

When calling a generic method you can either explicitly tell the compiler what type to use for the generic type parameter, or you can have the compiler deduce the type from the actual argument types.

var
Foo: TFoo;
begin
Foo := TFoo.Create;
Foo.GenericMethod<integer>(42);
Foo.GenericMethod(13);
end;

Generic methods can be declared both in generic types or in plain non-generic types.

type
// Generic method - of generic class
TGenericClass<T, U>= class
procedure GenericMethod<V>(A: T; B: U; C:V);
end;

procedure TGenericClass<T, U>.GenericMethod<V>(A: T; B: U; C: V);
begin
end;

Although, one might think that having support for generic properties as well would be a natural extension (after all a property just maps to getter and setter methods, after all), but indeed this is not possible. I don't think C# supports this, either, btw.

type
// Generic property - not supported
TBar = class
procedure SetGenericProperty<T>(const A: T);
function GetGenericProperty<T>: T;
// NOTE: Is this not allowed - Would be cool?!
property GenericProperty<T>: T read GetGenericProperty<T> write SetGenericProperty<T>;
end;

Generic interfaces


Interfaces can now also be declared with generic type parameters. This allows generic classes implement a whole class of interfaces (for all the possible type parameters).

type  
IGenericInterface<T> = interface
procedure Handle(A: T);
end;

Using generic interfaces instead of plain interfaces can improve performance, because you don't have to sort to late-bound checks and casts (or boxing) to and from TObject.


The .NET 2.0 specific System.Collections.Generic namespace provides a number of generic interfaces that the generic collection classes use and implement, such as IComparer<T> and IEnumerable<T>.


Generic records


Just as you can have generic classes, you can have generic records. They work exactly the same, the difference is that records implement value types that are stored inline and not via a reference.

type
TGenericRecord<T> = record
Field: T;
procedure DoSomething(A: T);
function GetSomething: T;
property SomeThing: T read GetSomething write DoSomething;
end;

procedure TGenericRecord<T>.DoSomething(A: T);
begin
Writeln(TObject(A).ToString);
end;

function TGenericRecord<T>.GetSomething: T;
begin
Result := Default(T)
end;

This makes it possible to write low- or zero-cost (combined with inlining) encapsulations of generic field wrappers, for instance.


Again, generic methods on records (normal or generic) is supported.

type
TAnyRecord = record
procedure DoSomething<T>(A: T);
end;

procedure TAnyRecord.DoSomething<T>(A: T);
begin
Writeln(TObject(A).ToString);
end;

Generic event types


Method pointers or event types can also include generic type parameters now.

type
// Generic event type
TOnGetValue<T> = procedure (Sender: TObject; var Value: T) of object;

You can use this event type in any normal class, by providing a concrete type parameter, or you can use it inside a generic classs forwarding the class' type parameter.

type
// Using a generic event type
TBar<T> = class
private
FOnGetValue: TGetValueEvent<T>;
public
property OnGetValue: TGetValueEvent<T> read FOnGetValue write FOnGetValue;
end;

Constraints


None of the examples so far has had any constraints on their generic type parameters. This means that you can supply any type arguments for these classes (i.e. integer, IInterface, TObject, TMyObject, boolean, etc) - so it gives you maximum flexibility. The downside is that you cannot do much with the values of generic type parameters - other than storing them, passing them in parameters and comparing or assigning the default value.


Unlike C++ templates, .NET Generics is an explicitly constrained system where you cannot perform any operations on generic parameters and values unless there are one or more "rules" (or constraints) on what the type parameter. In C++ the constraints are implicit; if you try to instanciate an illegal combination of template type parameters, you typically get long and cryptic error messages.


In Delphi.Net the constraints are specified by using a colon following by one or more constraints (much like you use Name: Type pattern for normal parameters, you use a TypeName: Constraints syntax for generic type parameters).

type  
// Generic type constraints
TConstrainedGeneric<
T; // No constraints
U: class;
V: constructor;
W: record;
X: TFoo;
Y: class, IMyInterface;
Z: IGenericInterface<T>> = class
end;

As you can see above, there are several types of constraints and most of them are self-explanatory and can be combined in the expected ways (record is incompatible with class and constructor, constructor implies class, and so on). You can constrain a type parameter to a specific class type or descendants (as TFoo above), but not a specific record type.


In .NET both classes and records can implement interfaces, so they are the most generic [sic] type of constraints and often used to call specific methods inherited from the interface.


Default values


When writing generic code, you'll often find the need to compare or assign the default value of the type parameter (nil for classes, all zero bits for records), but until now there has been no syntax to do this. In Win32 you could use FillChar, but this is not type safe (and thus not supported in .NET) and is not a function you can use in expressions.


There is now a new intrinsic function called Default that takes a generic type parameter and returns an the default value (nil or zero-filled record).

procedure TFoo<T>.GetValue(Sender: TObject; var Value: T); 
begin
Writeln('Before:', TObject(Value).ToString);
Value := Default(T);
Writeln('After:', TObject(Value).ToString);
end;

Default(T) can be used on all type parameters, even if they have no constraints.


Creating instances of type parameters


With one or more constraints specified on the type parameters, you can start to do more interesting things with the type values. For instance, a type that has a constructor constraint can be used to create new instances of the type.

type  
// Creating an instance of a type parameter
TCreateGeneric<V: constructor> = class
private
FV: V;
public
procedure Test;
end;

procedure TCreateGeneric<V>.Test;
begin
FV := V.Create;
end;

Without the constructor constraint on the V type parameter, this code does not compile;



[DCC Error] TestGenerics1.dpr(134): E2018 Record, object or class type required


Conclusion


There is much more to the RAD Studio 2007 version than I have room for here - take a look at some other beta bloggers, Nick's blog and information at CodeGear's site.


There is no doubt that this is the best and most functional version of Delphi/BDS/RAD Studio ever. It includes updated versions of the Delphi Win32 and C++Builder personalities, and brings the Delphi for .NET personality a whole generation step forward.


If you are still on the fence with an older Win32-only version of Delphi (D7 and earlier), upgrading now is a no-brainer. Consider getting Software Assurance as well. If you are a .NET programmer, this is a must-have release, of course. Getting no-fuss access to .NET 2.0 including powerful support for consuming and creating generic types is finally here.


 If you are only interested in Win32 programming and you already have Delphi 2007 with no SA, you may want to consider waiting until the next version that (according to the roadmap) should bring generics to Win32. Or better yet, upgrade now, get SA and start playing with and learning generics - it can quickly become addictive :).



Copyright © 2004-2007 by Hallvard Vassbotn