Using Pascal/Delphi with R

Contents

Back to main list

Creating a DLL

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.


Building a package

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.

Calling R functions

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';


Calling conventions

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;


Preserving registers

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:

  1. 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.

  2. After any operation that may have changed the status word, put this call before returning to R:

      Set8087CW(Default8087CW);

  3. 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;

Debugging

To allow source level debugging of your code:

  1. Under Project|Options...|Linker, change the image base to $15000000 (or some other value that is unlikely to conflict with another DLL).
  2. 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.
  3. Set any desired breakpoints in your code.
  4. Hit F9 to compile your DLL and run R.
  5. 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:


Multidimensional arrays in Delphi

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