|
|
(66 intermediate revisions by 3 users not shown) |
Line 1: |
Line 1: |
− | == Introduction ==
| |
| | | |
− | Delphi and Pascal come from another language family than D. D uses the C syntax. In short, a C syntax uses more symbols and more operators.
| |
− | The following document develops a few points over which D and Delphi/Pascal have some similarities or ambiguities.
| |
− |
| |
− | == Types equivalence ==
| |
− |
| |
− | {| border="1" cellpadding="6" cellspacing="0"
| |
− | | '''Pascal'''
| |
− | | '''D'''
| |
− | | '''notes'''
| |
− | |-
| |
− | | Pointer
| |
− | | void*
| |
− | |
| |
− | |-
| |
− | | Byte
| |
− | | ubyte
| |
− | | 8 bit unsigned integer. note the ambiguity when coming from Pascal
| |
− | |-
| |
− | | ShortInt
| |
− | | byte
| |
− | | 8 bit signed integer
| |
− | |-
| |
− | | Word
| |
− | | uint16
| |
− | | 16 bit unsigned integer
| |
− | |-
| |
− | | SmallInt
| |
− | | int16
| |
− | | 16 bit signed integer
| |
− | |-
| |
− | | DWord / Cardinal
| |
− | | uint
| |
− | | 32 bit unsigned integer
| |
− | |-
| |
− | | Integer
| |
− | | int
| |
− | | 32 bit signed integer
| |
− | |-
| |
− | | UInt64
| |
− | | ulong
| |
− | | 64 bit unsigned integer
| |
− | |-
| |
− | | Int64
| |
− | | long
| |
− | | 64 bit signed integer
| |
− | |-
| |
− | | Single
| |
− | | float
| |
− | | IEEE 754 32 bit floating point
| |
− | |-
| |
− | | Double
| |
− | | double
| |
− | | IEEE 754 64 bit floating point
| |
− | |-
| |
− | | NativeUInt
| |
− | | size_t
| |
− | | library type, either an alias to a unsigned 32 bit integer or to a unsigned 64 bit integer
| |
− | |-
| |
− | | NativeInt
| |
− | | ptrdiff_t
| |
− | | library type, either an alias to a signed 32 bit integer or to a signed 64 bit integer
| |
− | |}
| |
− |
| |
− | == The object model ==
| |
− |
| |
− | The object model is striclty equivalent. Multiple inheritence of classes is not allowed but can be achieved with interfaces.
| |
− |
| |
− | * Delphi, Pascal
| |
− |
| |
− | <pre>type ISome = interfaca
| |
− | procedure blah();
| |
− | end;
| |
− |
| |
− | type TThing = class
| |
− | end;
| |
− |
| |
− | type TSomething = classs(TThing, ISome)
| |
− | procedure blah();
| |
− | end;</pre>
| |
− |
| |
− | * The D2 way:
| |
− |
| |
− | <pre>interface ISome(){
| |
− | void blah();
| |
− | }
| |
− |
| |
− | class TThing{
| |
− | }
| |
− |
| |
− | class TSomething: TThing, ISome{
| |
− | void blah(){}
| |
− | }</pre>
| |
− | == Memory management ==
| |
− |
| |
− | Memory management is one of the biggest difference encountered when learning D with a Pascal background. This is less an aspect of the language than from the runtime. D uses a garbage collector (GC) while Pascal and traditionnal Delphi (before the switch to llvm ARC) uses manual memory management.
| |
− |
| |
− | For example the following program in Pascal leaks a TObject.
| |
− | <pre>
| |
− | program program1;
| |
− | var
| |
− | obj: TObject;
| |
− | begin
| |
− | obj := TObject.Create;
| |
− | // obj.Free; // leak
| |
− | end;
| |
− | </pre>
| |
− |
| |
− | While the D equivalent does not, because the Object reference is collected:
| |
− | <pre>
| |
− | void main(string[] args){
| |
− | Object obj = new Object; // new causes collection
| |
− | // the GC will delete Obj automatically
| |
− | }
| |
− | </pre>
| |
− |
| |
− | However several technics exist to override the GC. For example it's possible to allocate a heap chunk which is not known by the GC, in the same fashion as done in Pascal with '''GetMem''', '''ReallocMem''' and '''FreeMem''':
| |
− | <pre>
| |
− | import std.c.stdlib;
| |
− | auto chunk = malloc(4096);
| |
− | scope(exit) free(chunk);
| |
− | // some code...
| |
− | </pre>
| |
− |
| |
− | which is equivalent to
| |
− | <pre>
| |
− | var
| |
− | chunk Pointer;
| |
− | begin
| |
− | chunk := GetMem(4096);
| |
− | try
| |
− | // some code...
| |
− | finally
| |
− | FreeMem(chunk);
| |
− | end;
| |
− | end;
| |
− | </pre>
| |
− |
| |
− | == Templates ==
| |
− |
| |
− | Traditional Delphi and Pascal didn’t supported templates. Delphi supports them since D2009, using the most common syntax: the left/right angle brackets (declaration and instantiation of a template).
| |
− |
| |
− | D uses parens for the declaration and the exclamation mark ('''!''') for the instantiation. Parens are used after the '''!''' if the template expects several parameters.
| |
− |
| |
− | * Delphi or FPC with the dialect ''{$Mode Delphi}''
| |
− |
| |
− | <pre>type foo<T,G,A> = class
| |
− | end;
| |
− |
| |
− | type fooccc = class(foo<char, char, char>)
| |
− | end;
| |
− |
| |
− | var
| |
− | bar: foo<integer,single,double>;</pre>
| |
− |
| |
− | * FPC with the dialect ''{$Mode objfpc}'' (aka '''''object''''' Pascal)
| |
− |
| |
− | Faithfully to the Pascal tradition, the objfpc dialect recquires two explicit keywords for the template declaration and the template instantiation: '''generic''' and '''specialize''':
| |
− |
| |
− | <pre>type generic foo<T,G,A> = class
| |
− | end;
| |
− |
| |
− | type fooccc = class(specialize foo<char, char, char>)
| |
− | end;
| |
− |
| |
− | var
| |
− | bar: specialize foo<integer,single,double>;</pre>
| |
− |
| |
− | * the D2 way:
| |
− |
| |
− | <pre>class foo(T,G,A){
| |
− | }
| |
− |
| |
− | class fooccc: foo!(char, char, char){
| |
− | }
| |
− |
| |
− | foo!(int, float, double) bar;</pre>
| |
− |
| |
− | == Conditional compilation ==
| |
− |
| |
− | D conditional compilation is one of the unique feature of the lanquage. While in Pascal you used a custom definition like this:
| |
− |
| |
− | <pre>a := a shr 3;
| |
− | {$IFDEF MYSWITCH}
| |
− | // some conditionally compiled code
| |
− | {$ENDIF}</pre>
| |
− | in D the equivalent use a standard language construct '''''version(Identifier)''''':
| |
− |
| |
− | <pre>a =>> 3;
| |
− | version(MYSWITCH)
| |
− | {
| |
− | // some conditionally compiled code
| |
− | }</pre>
| |
− | But now you’ll get more. A feature that doesn’t exist in Pascal and Delphi is the '''''static if''''' expression. It allows a more advanced conditional compilation, particularly in the templates, since it the type of the template parameters can be tested when it gets instantiated.
| |
− |
| |
− | <pre>struct foo(T)
| |
− | {
| |
− | // something is only compiled if T type is string.
| |
− | static if (is(T == string)){
| |
− | void something(){}
| |
− | }
| |
− | }</pre>
| |
− | This is also related to the template constraints (the constraint is applied to the whole template while in our previous example it’s only applied to the enclosed expressions). You might know the principle if you’ve used Delphi XE6 or upper.
| |
− |
| |
− | == Inclusion ==
| |
− |
| |
− | D has a feature similar to Pascal/delphi source inclusion.
| |
− |
| |
− | for the example the following inclusion:
| |
− |
| |
− | <pre>procedure Something;
| |
− | begin
| |
− | {$IFDEF DEMO}
| |
− | {$I myDemoImplementation.inc}
| |
− | {$ELSE}
| |
− | {$I myFullImplementation.inc}
| |
− | {$ENDIF}
| |
− | end;</pre>
| |
− | is rewritten in D
| |
− |
| |
− | <pre>void something()
| |
− | {
| |
− | static enum demoCode = import(myDemoImplementation.d);
| |
− | static enum fullCode = import(myFullImplementation.d);
| |
− | version(DEMO){
| |
− | mixin(demoCode);
| |
− | } else {
| |
− | mixin(fullCode);
| |
− | }
| |
− | }</pre>
| |
− |
| |
− | == RTTI, published attribute and properties. ==
| |
− |
| |
− | In Pascal and Delphi, the Runtime Type Informations (RTTI) are an important feature used by the TPersistent and the TComponent system, as well as for custom serialization or for software settings or even Object dumping into a database.
| |
− |
| |
− | D has no equivalent feature. The '''published''' attribute does not exist but instead you have some compile-time reflection (traits, std.traits, user defined attributes) which could be used to design a similar system.
| |
− |
| |
− | Pascal properties are deeply linked to the RTTI mechanisms. D properties are a bit different and even more powerfull. They allow to use the assign operator instead of calling the setter with parens (so far this is a common behaviour) but they also can be '''overloaded'''.
| |
− |
| |
− | A classic, non published, Pascal property (interface section only):
| |
− |
| |
− | <pre>private
| |
− | FField: integer;
| |
− | procedure SetField(AValue: Integer);
| |
− | public
| |
− | property Field: integer read FField write SetField;</pre>
| |
− | The D equivalent, with overload:
| |
− |
| |
− | <pre>private:
| |
− | int fField;
| |
− | public:
| |
− | @property void field(int aValue){fField = aValue;}
| |
− | @property void field(string aValue){fField = to!int(aValue); }
| |
− | @property int field(){return fField;}</pre>
| |
− |
| |
− | == Sets and the "in" operator ==
| |
− |
| |
− | In Pascal and Delphi you used the '''in''' operator for testing the presence of a value in a set. D also have this operator but its usage is different, it’s used to test the presence of a value in an associative array (AA):
| |
− |
| |
− | <pre>auto asar = [
| |
− | 8 : "AA",
| |
− | 15 : "AB",
| |
− | 32 : "AC"
| |
− | ];
| |
− | assert(8 in asar);
| |
− | assert(!(57 in asar));</pre>
| |
− | The Pascal syntax used to define a set does not exist in D, for example the following expression has no simple equivalence:
| |
− |
| |
− | <pre>const THexChars Set Of Char = ['0'..'9','a'..'f','A'..'F'];
| |
− |
| |
− | function IsHexChar(C: Char): Boolean;
| |
− | begin
| |
− | result := C in THexDigits;
| |
− | end;</pre>
| |
− | Additionally to the '''in''' difference, double dots '''..''' (slice operator) are not as flexible as in Pascal. The left hande side and the right hand side must be some integral indexes.
| |
− |
| |
− | However, since D has operator overloading, it’s possible to extand the usage of the '''in''' operaotr and the slice operator in a custom type. for example:
| |
− |
| |
− | <pre>import std.stdio;
| |
− | import std.traits;
| |
− |
| |
− | struct bitWise(T) if (isIntegral!T){
| |
− | T theValue;
| |
− | alias theValue this;
| |
− | bool opIn_r(T aValue){
| |
− | return (aValue & theValue) >= aValue;
| |
− | }
| |
− | }
| |
− |
| |
− | void main(string args[]){
| |
− | bitWise!ubyte bw;
| |
− | bw = 0b11110000;
| |
− | assert( 0b11100000 in bw );
| |
− | assert( !(0b00001111 in bw));
| |
− | }</pre>
| |
− |
| |
− | == The "is" operator ==
| |
− |
| |
− | The keyword '''is''' exists in the two languages but have a different meaning.
| |
− |
| |
− | In Pascal, '''is''' is used to test if a class inherits from a particular ancestor type. For example
| |
− | <pre>
| |
− | procedure TMyClass.Event(Sender: TObject);
| |
− | begin
| |
− | if (Sender is TButton) then with TButton(Sender) do begin
| |
− | // some code related to Sender as TButton...
| |
− | end;
| |
− | if (Sender is TCheckBox) then with TCheckBox(Sender) do begin
| |
− | // some code related to Sender as TCheckBox...
| |
− | end;
| |
− | end;
| |
− | </pre>
| |
− | In D, the same can be achieved by comparing the result of a cast to '''null''':
| |
− | <pre>
| |
− | void event(Object sender){
| |
− | Button button = cast(Button) sender;
| |
− | Checkbox checkbox = cast(Checkbox) sender;
| |
− | if(button){/*some code related to sender as Button*/}
| |
− | if(checkbox){/*some code related to sender as Checkbox */}
| |
− | }
| |
− | </pre>
| |
− |
| |
− | In D, '''is''' is used to test the equivalence of two types, at compile-time. It's often used in the template constraints, for example:
| |
− |
| |
− | <pre>
| |
− | template myTemplate(T) if ((is(T == float)) | (is(T == double)))
| |
− | {
| |
− | T functionA(T param0, T param1){return T.init;}
| |
− | T functionB(T param0, T param1){return T.init;}
| |
− | }
| |
− | // compile time error, int does not verify ((is(T == float)) | (is(T == double)))
| |
− | auto value = (myTemplate!int).functionA(1,2);
| |
− | </pre>
| |