This chapter describes SWIG's support of Delphi (Object Pascal). You should be familiar with the basics of SWIG, especially typemaps.
The Object Pascal support is early stage but it is able to convert plain C API as well as C++ classes and to wrap them in Object Pascal classes.
For any comment and suggestion please contact me:
Dr. Eng. Stefano Moratto
My work had been started from the Modula-3 module. I would like to thanks the author for its job. It has been a valuable start for this project.
The Pascal language has a long history but now it is not widely used like C++ Java.
Its most used implementation is CodeGear (aka Borland) Delphi: it has a very productive IDE, an impressive components library and a big market of open source and commercial components. It implements a variation of Pascal, the Object Pascal. It has all the features, except for generic programming (from version 2009 this is not true anymore), of the other languages around the world. Delphi targets only Windows platform.
There is an emerging implementation that targets many platform: Free Pascal.
This Swig module targets Delphi Win32 and Free Pascal using (-Sd) Delphi mode. I think it would be very interesting for the Free Pascal community.
Although it is no problem to write Object Pascal programs that performs as fast as C most libraries are not written in Pascal but in C. Fortunately the binary interface of most function libraries can be addressed by Pascal. Even more fortunately even non-C libraries may provide C header files. This is where SWIG becomes helpful.
The C headers and the possibility to interface to C libraries still leaves the work for you to write Pascal interfaces to them.
SWIG converts C headers to Pascal interfaces for you. You could call the C functions without loss of efficiency but it won't be joy because you could not pass String (without a cast to PChar)or open arrays and you would have to process error return codes rather then exceptions. But using some typemaps SWIG will also generate wrappers that bring the whole Pascal comfort to you. If the library API is ill designed writing appropriate typemaps can be still time-consuming. E.g. C programmers are very creative to work-around missing data types like (real) enumerations and sets.
But you have still a problem: C library interfaces are often ill. They lack for certain information because C compilers wouldn't care about. You should integrate detailed type information by adding typedefs and consts and you should persuade the C library programmer to add this information to his interface. Only this way other language users can benefit from your work and only this way you can easily update your interfaces when a new library version is released. You will realise that writing good SWIG interfaces is very costly and it will only amortise when considering evolving libraries.
Without SWIG you would probably never consider to call C++ libraries from Pascal. But with SWIG this is worth a consideration. SWIG can write C wrappers to C++ functions and object methods. In fact it breaks down C++ libraries to C interfaces which can be in turn called from Pascal. To make it complete you can hide the C interface with Pascal classes and exceptions.
The purpose of this module is to allow Pascal programmer to leverage the huge amount of open source C/C++ code available. The C/C++ libraries must have to be compiled as DLL. The swig module generates import units for the exported function and variable, const and typedefs.
Basically the produced units are a list of procedures /functions with the external statement.
Codegear’s Pascal as well as FreePascal have an integrated support for calling C functions. This is also extensively used by the standard Pascal libraries to call OS functions. The Pascal part of SWIG and the corresponding SWIG library delphi.swgcontains code that uses these features. Because of the built-in support there is no need for calling the SWIG kernel to generate wrappers written in C. All conversion and argument checking can be done in Pascal and the interfacing is quite efficient. All you have to do is to write pieces of Pascal code that SWIG puts together.
For each DLL you want to use in Pascal you have to write a interface file like dllapi.i. Given this interface file SWIG produces a modulename.pas file and a wrap.c (or .cpp). The modulename is obtained by the %module directive contained in the .i file.
E.g.
// dllapi.i %module API
It produces:
unit API; interface implementation initialization finalization end.
Wrap.c(.cpp) contains helper function that are generated in order to wrap C/C++ items into exportable functions. Its name can be overridden by the wrapdllname command line parameter.
The modulename.pas files contains:
Since the objective of this SWIG module is to import C/C++ functions packaged in DLLs into Delphi the main feature is the ability to write import declaration.
E.g.
int add (int a, int b);
it is translated in:
//Unit's interface section function add(a, b : integer) : integer; stdcall;
and in:
// Unit's implementation section function add(a, b : integer) : integer; stdcall; external __DLLNAME name 'add';
The SWIG module emits two declarations in the generated module’s unit: one in the interface section and one in the implementation section. In the implementation the function is declared as an external function contained in a DLL named __DLLNAME.
__DLLNAME is variable declared in the beginning of the implementation section. Its default value is “modulename.dll”, but it can be overridden using del –dllname command line switch.
The calling convention is stdcall. It is typical of function exported by DLL. It can be overridden using the DELPHI pragma CALLCONV.
Each structure is declared in Pascal as an opaque pointer.
E.g.
typedef struct tagMyStruct { int a } MyStruct;
it is translated in:
type MyStruct = type pointer;
This means that every type’s instance is dynamically allocated. For each type SWIG produces a pair of functions( New_TYPE and Delete_Type) used for allocating and deallocating). For every field of the structure a get/set pair of functions are declared.
function New_MyStruct : MyStruct; procedure Delete_MyStruct (Self:MyStruct); procedure MyStruct_a_set (Self:MyStruct, value : integer); function MyStruct_a_get (Self:MyStruct) : integer;
We can convert in Pascal every kind of C declaration but we may encounter problems of structure packing, field alignment and case of mixing instance allocation (e.g. an instance may be allocated by the C library but deallocated in Pascal, C and Pascal have different memory manager). So it is safe and easer to consider only opaque pointer like most of the Windows API. For example consider the HDC or HWND type.
Each C/C++ const (or define) is declared in Pascal using the const statement. When the translation from C to Pascal is too complicated a const can be declared using the %feature "delphi:runtime_const". When it is enabled a const is declared as a variable and its value is initialized at runtime in the initialization section of the unit.
E.g.
#define C (some very complicated expression)
It is translated in
var C : integer; initialization C := c_get();
Each C/C++ enum is declared in Pascal using a integer for the enum type and const statements for the enum values. When the translation from C to Pascal is too complicated the enum values can be declared using the %feature "delphi:runtime_const". (See const declaration). The C enum are not translated in Pascal enum because of the binary size of an enum is different between C and Pascal (although it could be resolved using pascal compiler directives)
E.g.
enum Color {RED, BLUE, GREEN};
is translated in:
type Color = type integer; const Color_RED= 0; const Color_BLUE= 1; const Color_GREEN= 2;
Interfaces to C++ files are much more complicated. As in the case of C struct, a class is handled as an opaque pointer:
A C++ class is composed by instance methods ( object function / procedure), variables ( fields), static method (class function/procedure), constructor and destructor. Each of those items are wrapped into plain C function allowing to be called by external programs.
E.g.
class MyClass { void f1(int a); int var1; };
It is wrapped in:
void MyClass_f1(MyClass * Self, int a); int MyClass_var1_set(MyClass * Self, int var1); void MyClass_var1_get(MyClass * Self);
This plain C API is imported as we have seen in previous section.
The C++ classes can be reconstructed in Pascal creating a TObject derived class that wraps the C API:
TMyClass = class (TObject, IUnknown) private FCObjPtr : MyClass; FOwnCObj : Boolean; protected // IUnknown ... public property CObjPtr : MyClass ... ; property OwnCObj : Boolean ... ; contructor Create(FCObjPtr : MyClass; OwnCObj : Boolean); overload; contructor Create(); overload;destructor Destroy(); virtual; // Wrapped C++ API procedure f1(a:integer); property var1 : integer ... ; end;
This kind of Pascal class is called “proxy” class in SWIG.
Each method calls the correspondent C function passing the FCObjPtr as the self parameter that is the instance’s pointer:
E.g.
contructor TMyClass.Create(); begin inherited Create; FCObjPtr := New_MyClass(); FOwnCObj := true; end; destructor TMyClass.Destroy(); begin if (FOwnCObj) and (FCObjPtr <> nil) then begin Delete_MyClass(FCObjPtr); FCObjPtr := nil; FOwnCObj := false; end; Inherited; end; procedure TMyClass.f1(a:integer); begin MyClass_f1(FCObjPtr, a); end;
The ownership of the object is specified by the FCOwnObj property.
In order to use a template in pascal code it has to be instantiated using concrete type for all the template types. Once it is instantiated it is exposed though SWIG as all the normal functions / classes.
SWIG supports template via the %template directive. In order to generate correct code in pascal it is required to use the macro
TEMPLATE_WRAP0(template class/function, instance name, concrete template type0, concrete template type1, …);
This macro calls the %template directive and creates some typemaps needed by the pascal generator.
The ability to override C++ virtual function in Pascal is provided by the SWIG feature called director. The basic idea is to define for each class a COM interface that contains all the C++ virtual functions. The Pascal proxy class should implements this interface. It is still in development.
Wrapping C++ libraries arises additional problems:
This module has been tested using Delphi 7 but it should be compatible with the FreePascal.
There are some experimental command line options that prevent SWIG from generating interface files. Instead files are emitted that may assist you when writing SWIG interface files.
Delphi specific | Description |
-noproxy |
no pascal proxy classes are generated for c++ classes. Valid only for c++ mode. |
-callconv |
It specifies the calling convention used for the declaration of the imported function. Valid values are stdcall or cdel. The default is stdcall (eg : function foo() ; cdel; external DLLNAME; ) |
-dllname |
It specifies the name of the dll used for the declaration of the imported functions. (eg : function foo() ; stdcall; external DLLNAME; ) |
-wrapdllname |
It specifies the name of the dll used for the declaration of the imported functions from the module’s generated helper code. (eg : function new_bar() : bar ; stdcall; external WRAPDLLNAME name “Delphi_new_bar”; ) |
-runtime_const |
All constants and enum values are obtained at runtime calling exported functions. |
Each C procedure has a bunch of inputs and outputs. Inputs are passed as function arguments, outputs are updated referential arguments and the function value.
Each C type can have several typemaps that apply only in case if a type is used for an input argument, for an output argument, or for a return value. A further typemap may specify the direction that is used for certain parameters. I have chosen this separation in order to be able to write general typemaps for the typemap librarydelphi.swg. In the library code the final usage of the type is not known. Using separate typemaps for each possible use allows appropriate definitions for each case. If these pre-definitions are fine then the direction of the function parameter is the only hint the user must give.
The typemaps specific to Pascal have a common name scheme: A typemap name starts with "pas", followed by "raw" or "wrap" depending on whether it controls the generation of the imported functions or for the wrapping classes, respectively. It follows an "in" for typemaps applied to input argument, "out" for output arguments, "arg" for all kind of arguments, "ret" for returned values.
The primary typemaps are:
pasrawtype |
It is used to define types for global const values / enum values. E.g. const float t = 1; it is translated in const t : single = 1; |
pasrawintype |
It is used to declare function’s input parameter type. E.g. void foo( int x) int is translated to integer in pascal and the declaration is: procedure (x : integer) |
pasrawinmode |
It is used to declare function’s input parameter modified (var | const | out). E.g. void foo( int & x) & is translated to var in pascal and the declaration is: procedure (var x : integer) |
pasrawouttype |
Not currently used |
pasrawoutmode |
Not currently used |
pasrawrettype |
It is used to declare function’s return value type. E.g. int foo( ) int is translated to integer in pascal and the declaration is: function foo : integer; |
The raw function/procedure declaration schema is :
function | procedure FXNAME (pasrawinmode parameter_name_i : pasrawintype, …. ) [ : pasrawretype ];
Some typemaps are used to wrap C++ classes in Pascal proxy classes. They are used for class declaration and definition and for calling imported functions inside pascal classes.
The following typemaps are used as a template to declare and implement a Object Pascal proxy class.
pasclassdef_base |
This is a template used to start a class declaration without base class |
pasvarrw |
This is a template used to declare a class read/write property |
pasvarro |
This is a template used to declare a class read only property |
pasdefault_constructor_def |
This is a template used to declare a class default constructor |
pasdefault_constructor_code |
This is a template used to implement a class default constructor |
pasclassdef_derived |
This is a template used to start a class declaration with a base class |
pasclassdef_end |
This is a template used to end a class declaration |
pasdestruct_def |
This is a template used to declare a destructor of class without a parent |
pasdestruct_code |
This is a template used to declare a destructor of class that has a parent |
pasdestruct_derived_code |
This is a template used to implement a destructor of class that has a parent |
asgetcptr_intf |
This is a template used to declare the property that gives the class “C” object pointer. |
The following typemaps are used to implement the proxy class method.
paswraptype |
It is used to declare function’s return value type |
paswrapintype |
It is used to declare function’s input parameter type |
paswrapinmode |
It is used to declare function’s input parameter modified (var | const | out) |
pasin |
It is used to pass the parameter to function that is about to be called |
pasin_locals |
It is used to declare local temporary variables used for parameter transformation -> function_locals |
pasin_pre |
It is used to declare code to be applied to parameter before the function call -> pre_code |
pasin_post |
It is used to declare code to be applied to parameter after the function call -> post_code |
pasout |
It is used to declare code to be applied to the result of the function call -> out_code |
The generated code for a method of a proxy class is :
function | procedure TCLASSNAME.METHODNAME (paswrapinmode parameter_name_i : paswraptype, ... ) [ : paswraptype ]; function_locals begin pre_code plain function call ( pas_in ) post_code out_code end;
Pascal , enumerations, and sets are not used.
Declarations of C++ classes are mapped to TObject derived classes while it is tried to retain the access hierarch.
To add units to the uses clause of the generated unit it can be used the USES pragma.
%pragma(delphi) USES = "my_unit"
he above code add my_unit to the interface uses clause.
Exceptions are not yet supported.
Feature |
Example |
Description |
nowrap_function |
%feature("nowrap_function","1") |
his feature can be used to not wrap exported functions. It can be useful in case of having a well defined API library that does not need to have wrapped functions argument. |
runtime_const |
%feature("runtime_const","1") |
This feature can be used initialize constants and enum values at runtime. |
Pragma |
Example |
Description |
USES |
%pragma(delphi) USES="..."; |
<>Add units to the uses clauses. |
CALLCONV |
%pragma(delphi) CALLCONV="cdecl"; |
Set the function calling convention |
The generated unit's code can be customized in the .i file using the following sections of code that are injected in the .pas file:
The generated unit has this structure:
unit modulename; interface uses %interface_uses %{ %} %interface_begin %{ %} .... %interface_end %{ %} %interface_functions %{ %} %interface_functions_wrapper %{ %} implementation initialization uses %implementation_uses %{ %} %implementation_begin %{ %} .... %implementation_end %{ %} %implementation_functions %{ %} %implementation_functions_wrapper %{ %} %initialization %{ %} finalization %finalization %{ %} end;
The main purpose of this module is to import into Delphi external code contained in precompiled DLL. The default calling convention is STDCALL for exported function in Windows environment. Delphi fully supports this convention but problems may occur depending of which compiler has been used to produce the DLL. These problems are related to function name decoration: every compiler has its convention especially Visual C++.
For each exported using stdcall VC++ add the following decoration:
_FUNCTIONNAME@N,
Where N is the number of byte put onto the stack for parameters. The only way to remove _ and @N is to write a .DEF file containing all the exported functions name. This is time consuming and the DLL may not be compilable.
So I wrote a little perl script that extracst all the names from a DLL and patch the generated pas source adding _ and @N where necessary. It is stored in Lib/Delphi/dllnames_fixup.pl