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, OpenArrayCount values.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:
|
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
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
is made with TextTransformer
|
TextTransformer is made with Borland
CBuilder
|