Using Pascal/Delphi with R
Contents
Back to main list
Create a DLL in the Delphi IDE by using File|New...|DLL. This will create and open a .dpr file
containing a "library" module.
Export procedures from your module using the exports clause, e.g.
exports test;
where test is a procedures declared within the library module or within a unit that it uses.
The R build system up to version 1.7.1 assumes that all source files for DLLs are written in C, C++, or Fortran.
If your package doesn't include any files in the src directory with extensions *.c, *.f, *.cc, *.cpp
or *.C, it will assume you don't have a DLL, and won't build it.
To overcome this, you need to do two things. First, you need your own makefile in your package src directory,
called Makefile.win. This file should build the target DLL in the src directory. For example,
DCC32=/cygdrive/f/progra~1/borland/delphi5/bin/dcc32
foo.dll: foo.dpr foo1.dfm foo1.pas
$(DCC32) -m -E. foo
The second thing you need to do is to get a patched MakePkg file,
and put it in your src/gnuwin32 directory. (This step shouldn't be necessary after version 1.7.1.)
With these additions, the usual "Rcmd install foo" style of package installation should
work with your Delphi code.
Declare R functions using the external modifier, e.g.
function ismdi:longbool; cdecl; external 'R.dll';
function R_alloc(n,size:integer):pchar; cdecl; external 'R.dll';
Declare procedures using the "cdecl" modifier, e.g.
procedure test(var a:integer;var b:double;var c:double); cdecl;
To return string values, you must copy them into memory allocated by R. For example, the function
GetText below fills a vector with the string "hello":
function Rpchar(s:string):pchar;
begin
result := R_alloc(1,length(s)+1);
strpcopy(result,s);
end;
type
PCharArray = array[1..1000000] of pchar;
procedure GetText(var s:PCharArray;var num:integer); cdecl;
var
i : integer;
begin
for i:=1 to num do
s[i] := RPchar('hello');
end;
Delphi uses the same register preserving conventions as R, so this is mostly done
automatically. The only problem that is likely to arise is with the floating point processor
(FPU) registers. Some versions of Delphi change the FPU control word upon entry to
a DLL (but this is not true of Delphi 5); all Windows programs are at risk of changing
the registers whenever they call a sloppily written DLL, such as the ones that are
used in the common dialogs (File Open, etc.).
To protect against messing up R by changing the FPU control word, the following strategy works:
- On first entry to the DLL, set the control word to the standard R value, then
save that as the Delphi default. Put this code in the library module:
procedure Rwin_fpset; cdecl; external 'R.dll';
function Get8087CW:word;
begin
asm
fstcw result
end;
end;
begin
Rwin_fpset();
Set8087CW(Get8087CW);
end.
- After any operation that may have changed the status word, put this call before returning
to R:
Set8087CW(Default8087CW);
- The following function may be useful in debugging; it returns false when the
control word has been unexpectedly changed:
function Okay8087CW:boolean;
begin
result := ((Default8087CW xor Get8087CW) and not $E060) = 0;
end;
To allow source level debugging of your code:
- Under Project|Options...|Linker, change the image base to $15000000 (or some other value
that is unlikely to conflict with another DLL).
- Under Run|Parameters..., enter the path to the R GUI in the "Host Application" box,
along with any arguments you might use on the R command line.
- Set any desired breakpoints in your code.
- Hit F9 to compile your DLL and run R.
- Within R, execute code that loads your DLL and calls one of your functions.
Delphi should stop at the first breakpoint that gets executed.
Mark Bravington points out the following:
- When you run R in the debugger, Delphi will likely trap several errors in the
startup code. As of 1.7.0 the cause of these errors is known (a minor incompatibility
between R's memory manager and the MinGW startup code), but the only known fix is
to rebuild R using the standard memory manager (by commenting out the line
LEA_MALLOC=YES
in the src/gnuwin32/MkRules file). This is likely to result in a substantially slower
executable. Because the error appears to be harmless but difficult to fix,
it is not likely to be fixed soon.
- Delphi will start R with the working directory being the one where your DLL was
built. If you normally work in a different directory you'll need to do some fiddling
on startup; details are given in this message.
Mark Bravington has written Linking Delphi to S: memory management
and multi-dimensional arrays, describing his unit USEFUL.pas
for handling dynamically allocated arrays.
Last modified: May 23, 2003, by Duncan Murdoch