The most important translation services of Delphi2Cpp are listed briefly in the following. However, the translation results aren't guaranteed for all points always, since Delphi2Cpp works with a simplified registration of type information. This system doesn't always suffice for a correct translation in individual cases.


File organization


The interface part and the implementation part of a unit are in Object-Pascal put in one file. In C++ they become a header file and a source file. The header file is enclosed into a sentinel:

#ifndef testH
#define testH

...

#endif

and the source file uses the precompiled VCL:

#include <vcl.h>
#pragma hdrstop


extern variables


Variables declared in interface parts are qualified as extern in the C++ headers and their instances are included into the implementation cpp-files.

TokenList : TList = NIL;
->
extern TList TokenList;  // in the header file
TList* TokenList = NULL; // in the cpp -file




Uses clauses


References to other units become to include directives in C++ in which the files of the VCL get the extension "hpp" and the extension is "h" for the other header files.

uses                      ->   #include "classes.hpp" 
  Classes, TetraTypes;         #include "TetraTypes.h"

Case sensitivity


Expressions which are different only by case are regarded as identical in Delphi. Therefore an integrated preprocessor is executed before the real translation. The preprocessor replaces all later occurrences of expressions which are different from the first occurrence only by the notation by the notation found first. The preprocessor provides the conditional compilation of the code at the same time.


Comments

All comments are output essentially unchanged at the corresponding positions. Line comments remain totally unchanged, while bracketing is translated from "(*...*)" to "/*...*/.


Simple substitutions


Many key words and operators can be replaced one to one. There is a long list of such substitutions. A few examples are:

Delphi  C++ 
begin 
end 
record  struct 
property  __property 
:= 
== 
<>  != 
and  && 


Simple type identifiers

Simple type names for the special C++ expansions of the CBuilders are required now and then. While e.g. the type "cardinal " usually is translated as " unsigned int", this isn't permitted in the following context:

property testprop: cardinal read GetProp;

Delphi2Cpp therefore produces a type definition for a simple identifier:

typedef unsigned int unsignedint;
__property unsignedint testprop = { read = GetProp };


Rearrangements of expressions


Variable declarations are a typical example of a simple rearrangements:

Name : Typ; -> Typ Name;

A little bit more complicated rearrangements are required in instructions. E.g.

for factor := expr1 to expr2 do
->
for ( factor = expr1; factor != expr2; factor ++ )




String constants and single characters


The apostrophes of the string constants are replaced by quotation marks. The treatment of the characters is more difficult. Depending on context the apostrophes are left or replaced by quotation marks.


'1' :           ->    case '1' :
string_id + '1'	->    string_id + "1" 



Procedures and functions


Procedures are translated to void-functions:

procedure foo; -> void foo();


The translation of functions is more complicated, because there aren't return-statements in Object-Pascal. Instead, the return value is assigned to a variable "Result", which is implicitely declared in each function. In C++ this variable must be declared explicitly and returned at the end of the function. Also to the Exit-function has to be replaced by a return-statement in C++.


function foo(i : Integer) : bar;
begin                        
  Result := 0;              
  if i < 0  then             
    EXIT              
  else         
    Result := 1;                      
end;
->

bar __fastcall foo ( int i ) 
{                            
  bar result;                
  result = 0;                
  if ( i < 0 )               
    return result;           
  else                       
    result = 1;              
  return result;             
}                            

In addition, the function name itself acts as a special variable that holds the function’s return value, as does the predefined variable Result. So the same translation as above results from:

function foo(i : Integer) : bar;
begin 
  foo := 0;
  if i < 0  then
    EXIT
  else
    foo := 1;
end;

Calls of procedures and functions


In contrast to Delphi the calls of procedures and functions in C++ have to end with parenthesis even then, if no parameters are passed.


foo; -> foo();


Calls of inherited procedures and functions


For each class, which inherits from another a typedef is inserted into the C++ code, like

class foo: public bar {
typedef bar inherited;

So, if in Object Pascal "inherited" is followed by a method identifier, it can be translated easily to C++.

inherited.foo -> inherited::foo()

When "inherited" has no identifier after it, it refers to the inherited method with the same name as the enclosing method. In this case, inherited can appear with or without parameters; if no parameters are specified, it passes to the inherited method the same parameters with which the enclosing method was called. For example,

procedure foo.bar(b : BOOLEAN);
begin
inherited;
end;

->

void __fastcall foo::bar ( bool b )
{
inherited::bar( b );
}


Ancestors


If no ancestor type is specified when declaring a new object class, Delphi automatically uses TObject as the ancestor. In C++ this has to be made explicit.

TNewClass = class ...
->
class TNewClass : public System::TObject ...



Constructors


Constructors start with the keyword "constructor" in Object-Pascal and can have an arbitrary name. In C++ is the name of the of the class also the name of the constructor.

constructor classname.foo;
->   
__fastcall classname::classname ( )

Constructor of the base class


Delphi2Cpp tries to find the call of the constructor of a base class and brings it in C++ form.


constructor foo.Create(Owner: TComponent);
begin                   
  inherited Create(Owner);                                         
end;                                         
->

__fastcall foo::foo ( TComponent * Owner )
: inherited   ( Owner )                   
{                                         
}                                         

Addition of missing constructors


Unlike Delphi constructors of base classes cannot be called directly in C++. A new constructor has to be written in the derived class within which the constructor of the base class is called. Delphi2Cpp inserts missing constructors in C++ automatically, e.g.

inline __fastcall virtual TDerivedComponent(TComponent* Owner) : inherited(Owner) { }



Destructors


Destructors start with the keyword "destructor" in Object-Pascal and can have an arbitrary name. In C++ is the name of the of the class also the name of the destructor preceeded by the the character '~'.

destructor classname.foo;
->
__fastcall classname::~classname ( )

Destructor of the base class


Delphi2Cpp tempts to find calls of destructors of the base class and to comment them out in C++. Thereby is assumed that the destructor of the base class is virtual. This has to be checked by the user.



destructor foo.Destroy();
begin                                                         
  FreeAndNil(m_Messages);                               
  inherited Destroy;                
end;                                                            
->

__fastcall foo::~foo ( )             
{                                    
  FreeAndNil ( m_Messages );         
  // todo check:  inherited::Destroy;
}                                    

Creation of instances of classes


VCL classes have to be created with new in C++.


TList.Create(NIL); -> new TList(NULL);


You have to supplement the code by hand, to delete such objects at a suitable place.



with-statements

In C++ there are no with-statements. Therefore all elements must be completely qualified.


type TDate = record                              
  Day: Integer;                                 
  Month: Integer;                                
  Year: Integer;                                 
end;                                        

procedure test(OrderDate: TDate);
begin                                             
  with OrderDate do                        
    if Month = 12 then               
    begin                                   
      Month := 1;                  
      Year := Year + 1;   
    end                                        
    else                                       
      Month := Month + 1;   
end;                                            
->

struct TDate {                                
  int Day;                                    
  int Month;                                  
  int Year;                                   
};                                            

void __fastcall test ( TDate * OrderDate )    
{                                             
 /*with OrderDate do*/                        
 if ( OrderDate->Month == 12 )                
   {                                          
     OrderDate->Month = 1;                    
     OrderDate->Year = OrderDate->Year + 1;   
   }                                          
 else                                         
   OrderDate->Month = OrderDate->Month + 1;   
}                                             



Resource strings


Resource strings werden in C++ deklariert und über ein Makro geladen.


resourcestring
SIndexError = 'Index out of bounds: %d';
->
// "List Index out of bounds: %d"
extern PACKAGE System::ResourceString _SIndexError;
#define Testheader_SIndexError System::LoadResourceString(_SIndexError);



Special VCL-functions


There aren't some VCL functions in C++ and have to be expressed differently (incomplete list):

 Delphi   C++   Condition 
 Assigned( foo )   foo != NULL 
 Copy(foo, 1,3)   foo.SubString(1, 3)   for strings 
 Dec(i)   i-- 
 Dec(i,2)   i -= 2 
 Delete(foo, 1,3)   foo.Delete(1, 3)   for strings 
 Dispose(foo)   delete foo 
 Exclude(foo, bar)   foo >> bar   for sets 
 High(enum_type)   größter enum-Wert 
 Inc(i)   i++ 
 Inc(i,2)   i += 2 
 Include(foo, bar)   foo << bar   for sets 
 Length(foo)   foo.Length ( )   for strings 
 Low(enum_type)   kleinster enum-Wert 
 New(p)   p = new type_of_p   only works, if p is an identifier 
 Pos(foo, bar)   bar.Pos ( foo );   for strings 
 PAnsiChar(foo)   foo.c_str()   for strings 
 PAnsiChar(foo)   char(foo)   else 
 SetLength(foo, bar)   foo.SetLength ( bar );   for strings 


RegisterComponents


Since components are an important feature of Delphi, a special translation routine was made for their registration.


RegisterComponents('NewPage',[TCustom1, TCustom2]);
->
TComponentClass classes[2] = {__classid(TCustom1), __classid(TCustom2)};
RegisterComponents("NewPage", classes, 1);


Enumerated types


The explicit definition of enumeration types is easy to translate.

Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
->
enum Day {Mon, Tue, Wed, Thu, Fri, Sat, Sun };

However, an implicit definition is also possible in object Pascal within a variable declaration. It is decomposed for C++ into an explicit type definition and the real declaration of the variable. The name of the type is derived from the name of the unit by appending two underscores and a counter.

Day : (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
->
enum test__0 {Mon, Tue, Wed, Thu, Fri, Sat, Sun };
test__0 Day;



Ranges


Numeric ranges for the specification of the size of an array are reduced to a value at the translation into C++. The original limits are inserted in the translated code as a comment.

type foo = array [1..10] of Char
->
typedef char foo [ 10/* 1..10 */ ]

Numeric ranges for the definition of of the range of a type are left out at the translation.

TYearType = 1..12; -> typedef int /* as default range type */ TYearType; // 1 .. 12;

In other cases the range specifications are copied in the C++ code as they are in Delphi and must be adapted by hand.


Sets


A Delphi set is simulated in the C++ VCL by the class Set:

template<class T, unsigned char minEl, unsigned char maxEl>
class __declspec(delphireturn) Set;

MySet: set of 'a'..'z';
->
System::Set < char, 97, 122 > MySet;

If there is no explicit type-declaration of a set, as e.g. in:

MySet := ['a','b','c'];

a helping macro is created:

#define test__0 (System::Set< char, 0, 255 >() 
   << char ( 97 ) << char ( 98 ) << char ( 99 ) )

MySet = test__0;



in-operator


The in-operator of Delphi is replaced by the "Contains" function of the Set class in C++.


is-operator


Dem is-Operator zur dynamischen Überprüfung eines Typs in Delphi entspricht in C++ ein Test mit "dynamic_cast"

ActiveControl is TEdit  ->  std::dynamic_cast(ActiveControl)


Static arrays


Static arrays in C++ are declared siimilar as in Delphi:

TArray2 = array [1..10] of Char
->
typedef char [ 10 ] TArray2


In Delphi such an array can be passed to a function like:

procedure foo(var arr: Array Of double);

But such a function also accepts dynamic arrays. It cannot be decided by the signature of the function, by which of these two kinds of arrays it will be called. Per default Delphi2Cpp creates code for DynamicArray arguments.

Note:

If a static array is passed to the function, the code has to be corrected by hand. In this case a second parameter which indicates the topmost index value of the array, also has to be passed, because there are no corresponding functions in C++ to the Delphi functions "High" and "Length", by which tis index could be evaluated. (The lowest index of a C++ array always is 0.) So the C++ function, which accepts the static array looks like:

procedure foo(var arr: Array Of double);
->
void __fastcall foo (  double * arr, int arr_size );


Dynamic arrays


Dynamic array are simulated in the CBuilder C++ by the class DynamicArray:

template <class T> class DELPHIRETURN DynamicArray;


MyFlexibleArray: array of Real;
->
DynamicArray < double > MyFlexibleArray;

This class has the properties "Low", "High" and "Length". By the "Length" property, the size of the array can be changed.

A Delphi function accepts a dynamic array as parameter:

procedure Check(aSources : Array Of String);

Delphi2Cpp translates such a parameter as a reference to a dynamic array:

void __fastcall Check ( const DynamicArray< String > & aSources );


Array of const


The value of an array of const has to be represented by two values in C++: a pointer to a TVarRec and the Index of the last element of the array, which begins at the position which the pointer points to.


procedure foo(arr : array of const);

->

void __fastcall foo ( TVarRec* arr, const int Args_Size );


When such a functions is called, the macro ARRAYOFCONST is written into the C++ output. This macro is defined for the CBuilder C++ as:

#define ARRAYOFCONST(values) 
OpenArrayvalues, 
OpenArrayCountvalues.GetHigh()


Delphi2Cpp decides by the expected parameter type how the set argument is translated:


procedure foo(arr : array of const);
procedure bar(set : TCharSet);

foo(['hello', 'world']);
bar(['hello', 'world']);

->

#define test__0 (System::Set< AnsiString, 0, 255 >() 
  << AnsiString ( "hello" ) << AnsiString ( "world" ) )

void __fastcall foo ( TVarRec* arr, const int arr_size );
void __fastcall bar ( TStringSet set );

foo ( ARRAYOFCONST(( "hello", "world" )) );
bar ( test__0 );

If such a passed array is passed further to another function, then Delphi2Cpp takes care that the second parameter is also passed in the C++ code.

procedure foo(var arr: array of const);
begin
  bar( arr );
end;
->

void __fastcall foo ( TVarRec* arr, const int arr_size )
{
  bar ( arr, arr_size );
}


Default Array-Property


If a class has a default property, you can access that property in Object-Pascal with the abbreviation object[index], which is equivalent to object.property[index]. For C++ the abbreviated form is translated to longer notation: object->property[index]



Nested functions


There aren't nested functions in C++. The automatic translation of nested Delphi functions replaces the inner functions by new member functions. The parameters and the declared variables of the outer function are passed to these new functions.


function TNested.Test(iOuterParam, 
                      iTwiceParam : Integer): Integer;
var
 iFunctionVar : Integer;

 procedure NestedTest(iInnerParam, iTwiceParam : Integer);
 begin
 result := iFunctionVar + 
           iClassVar + 
           iOuterParam + 
           iInnerParam + 
           iTwiceParam;
 end;

begin
 iClassVar := 1;
 iFunctionVar := 2;
 NestedTest(3, 4);
 result := result + iTwiceParam;
end;
->

void __fastcall TNested::NestedTest ( int iInnerParam, 
                                      int iTwiceParam, 
                                      int& iOuterParam, 
                                      int& result, 
                                      int& iFunctionVar )
{
  result = iFunctionVar + 
           iClassVar + 
           iOuterParam + 
           iInnerParam + 
           iTwiceParam;
}

int __fastcall TNested::Test ( int iOuterParam, 
                               int iTwiceParam )
{
  int result;
  int iFunctionVar;
  iClassVar = 1;
  iFunctionVar = 2;
  NestedTest( 3, 4, iOuterParam, result, iFunctionVar );
  result = result + iTwiceParam;
  return result;
}

Initialization/Finalization


There isn't any direct counterpart for the sections "initialization" and "finalization" of a Unit in C++. These sections are therefore translated as two functions which contain the respective instructions. In addition, a global variable of a class is defined. In the constructor of this class the initialization routine is called and in destructor the routine for the finalization is called.



initialization

pTest := CTest.Create;

finalization

pTest.Free();

->

void Tests_initialization()
{
  pTest = new CTest;
}

void Tests_finalization()
{
  delete pTest;
}

class Tests_unit
{
public:
  Tests_unit(){ Tests_initialization(); }
  ~Tests_unit(){ Tests_finalization(); }
};
Tests_unit _Tests_unit;


Initializing arrays


Array's are initialized very simply in Delphi. The initialization of an array of TStyleRecord's:


TStyleRecord = record
  Name    : string;
  Style   : TFontStyles;
end;

could look like:


DefaultStyles : TStylesArray = (
 (Name : 'tnone';    Style :  []),
 (Name : 'tstring';  Style :  []),
 (Name : 'tcomment'; Style :  [fsItalic])
);

This was translated in the earlier versions of Delphi2Cpp as:


TStylesArray DefaultStyles = { 
     { "tnone" /*Name*/, arrays__0 /*Style*/ },
     { "tstring" /*Name*/, arrays__1 /*Style*/ },
     { "tcomment" /*Name*/, arrays__2 /*Style*/ } 
    };

It is a problem however, that such initializations are only possible, if the elements are built in types of C. Such initializations of elements of the type TFontStyle aren't permitted. Beginning with version 1.2.4., Delphi2Cpp therefore generates initialization functions which are called within the initialization routine of the corresponding unit (see above).


void DefaultStylesInit( )
{
  DefaultStyles[0].Name = "tnone";
  DefaultStyles[0].Style = arrays__0;
  DefaultStyles[1].Name = "tstring";
  DefaultStyles[1].Style = arrays__1;
  DefaultStyles[2].Name = "tcomment";
  DefaultStyles[2].Style = arrays__2;
}



Variant parts of records


There is only a makeshift to treat variant parts in records: all fields are listed in equal rank:


TRect = packed record
  case Integer of
    0: (Left, Top, Right, Bottom: Longint);
    1: (TopLeft, BottomRight: TPoint);
end;
->

struct TRect {
/* case Integer of */
/* 0 */ int Left, Top, Right, Bottom;
/* 1 */ TPoint TopLeft, BottomRight;
};

Unions would be candidates for a better translation of variant parts in C++.


Visibility of class members


In Delphi a private or protected member is visible anywhere in the module where its class is declared. In C++ a private or protected member is visible only in the class. So the tranlation makes all classes in the same module to friends of each other. But as problem remains, that an access to these elements from code in the same unit, but outside of the class is forbidden in C++.
In Delphi Members at the beginning of a VCL class declaration that don’t have a specified visibility are by default published and in other classes they are public. In C++ this is written explicite. (Delphi2Cpp ignores the {$M+} directive, which would make theese elements public.)



class-reference type


In Delphi operations can be performed on a class itself, rather than on instances of a class. With TClass references to classes can be declared and used. CBuilder has as counterpart to Delphi's TClass:

typedef TMetaClass* TClass

For example:

type

TMyClass = class of TFoo;
...

implementation

function TBar.GetClass : TMyClass;
begin
result := TMyClass;
end;

is translated to:

typedef TMetaClass* /* TFoo */ TMyClass;
...

TMetaClass * __fastcall TBar::GetClass ( )
{
TMetaClass * result;
result = __classid ( TMyClass );
return result;
}

But some of the possibilities of Delphi to operate with such class references are not translated correctly yet by Delphi2Cpp or even cannot be translated at all.



What is not translated (until now)


At first Delphi2Cpp is concentrated on the relatively simple code constructs occurring the most frequently in Delphi code. In more complex cases Delphi2Cpp even can fail at constructs described as translatable above. In the result, there may by some snippets of the original Delphi code within the C++ code. They have to be eliminated by hand. Sometimes Delphi2Cpp generates an explicit comment which points out with the word "todo" that here still is something to do.

Some general Delphi constructs, which aren't, automatically translated yet are:

  • The initialization part and the finalisation part have be reorganized by hand.
  • In Delphi (void*) is often casted to specific pointer types. C++ compilers produce error messages here, if the cast isn't made explicitly.
  • Variant types aren't translated yet. Unions would be candidates for this in C++.



Parse-tree


Starting point for Delphi2Cpp.tpp was the Delphi pretty-printer DelphiPrettyprint.tpp whose formatting actions mostly remain unchanged. The formattings are made, when the parsing tree is processed, which was created before. Mostly, the translations are already carried out for the construction of the tree.


Service

I make variants of Delphi2Cpp or also other translators adapted individually for you. The translation results can be increased drastically by such customizations. Please contact me by the contact form at:

Contact_form



   deutsch Deutsch

 
Latest News
03/24/10
Delphi2Cpp 1.2.4 Initialization and finalization of units [more...]

03/18/10
Delphi2Cpp 1.2.3 Diverse small improvements [more...]


 
This website is generated from plain text with [Minimal Website ]

Minimal Website
Minimal Website is made with TextTransformer

TextTransformer
TextTransformer is made with Borland CBuilder

  borland