FreeMAT Memo

Calling C(C++) Functions From FreeMAT(v4.0)

For faster computations using FreeMAT...

1 - A Basic Example

Do the following 4 steps:

1) foo.c - Defining C(C++) Functions:

#define __PORT __declspec(dllexport)

// Ex.1) Vector Operation (length N)
__PORT void addArrays(int N, float *a, float *b, float *c){
  int i;
   
  for(i=0;i<N;i++)
    c[i]=a[i]+b[i];
}

// Ex.2) Matrix Operation (size NxM)
__PORT void addMat(int N, int M, float __fastcall *a, float __fastcall *b, 
  float __fastcall *c){
  int i,j;
  
  for(i=0;i<N;i++)
    for(j=0;j<M;j++)
       c[i*M+j]=a[i*M+j]+b[i*M+j];
}

2) test1.m - Script M File for FreeMAT:

test1.m:

import('foo.dll','addArrays','addArrays','void','int32 N, float[N] a, float[N] b, float[N] &c');
import('foo.dll','addMat','addMat','void','int32 N, int32 M, float[N*M] a, float[N*M] b, float[N*M] &c');

a=[1,2,3,4]; b=[5,6,7,8]; c=zeros(1,length(a)); % c must be pre-defined
d=[1,2;3,4]; e=[5,6;7,8]; f=zeros(size(d,1),size(d,2)); % f must be pre-defined
 
addArrays(length(a),a,b,c);
addMat(size(d,1),size(d,2),d,e,f);
c,f

Notes

3) Making foo.dll - Using Borland C++ 5.5

 > bcc32 -WD -vu foo.c

This generates a shared library foo.dll in the current directory.

Notes

1) Clear from FreeMAT:
--> clear 'libs'
2) Unlock foo.dll (using freeware 'unlocker' etc.) from Windows OS (this still is required, due maybe to the bug of FreeMAT...) Or, simply restart FreeMAT.

4) Run test1.m on FreeMAT (assuming both foo.dll and test1.m are in the current directory/within path)

--> test1

ans =
  6  8 10 12
ans =
  6  8
 10 12

-->

2 - Return Scalar Variables To FreeMAT

Pointer variables cannot be returned to FreeMAT, but it is still possible to return a single value. The following function returns an integer value of ret to FreeMAT:

__PORT int foo(int a){
  int ret,b=3;
  ret=a*2+b;
  return ret;
}

In FreeMAT, you can retrieve the value as

--> import('foo.dll','foo','foo','int32','int32 a');
--> y=foo(3)

ans =
  9

-->

3 - Output to FreeMAT Console

As in the on-line manual for import command, you can output messages to FreeMAT console within C functions by the following codes:

typedef void (*strfunc)(const char*);
strfunc FreeMatText;

__PORT void freemat_io_handler(strfunc printFunc){
  FreeMatText = printFunc;
}

The above should be written in front of the following example code:

char msg[2048];

__PORT void foo1(){
  sprintf(msg,"Can you see me?\n");
  FreeMatText(msg);
}
__PORT void foo2(const char *str){
  FreeMatText(str);
}

Then, in FreeMAT, you can type

--> import('foo.dll','foo1','foo1','void','');
--> import('foo.dll','foo2','foo2','void','string str');
--> foo1();
Can you see me?
--> foo2('This appears in FreeMAT Console'); % somewhat it doesn't show ...
--> foo2(sprintf('This finally appears in FreeMAT Console!\n'));
This finally appears in FreeMAT Console!
--> foo2(sprintf('Year!\nYeeaarr!!\n'));
Year!
Yeeaarr!!
-->

Notes

4 - Retrieve String Data from C Functions

In order to retrieve the strings from C functions, the string data (i.e. char []) in C functions should first be converted in an integer array (i.e. by casting them), and then, retrieved using integer pointer (i.e. using int * instead):

__PORT void foo3(int N, int *str){
  char *b="[test1]\n[test2]";
  int i,n=strlen(b);
  
  if(n>N)n=N;
  for(i=0;i<n;i++)
    str[i]=(int)b[i];
}

In FreeMAT, you need to convert the integer type values obtained from the C function back into string data type using char command:

--> import('foo.dll','foo3','foo3','void','int32 N, int32[N] &s');
--> str=zeros(1,20);
--> foo3(length(str),str); char(str)

ans = 
[test1]
[test2]
-->

Notes

5 - Convert Strings From C (i.e. char *[]) To FreeMAT Cell

The easiest way may be to take the following three steps:

  1. Write the strings in a cell array format of FreeMAT.
  2. Convert all the strings (that are represented by char *[] in C) into
  3. an one-dimensional integer type array and return it to FreeMAT.
  4. Use a combination of eval and char functions in FreeMAT to re-convert
  5. into a cell array of strings.

An example C source:

char *str="{'test1';'test23'}"; // Example strings

__PORT int getstr(int flg, int N, int *s){
  int i,len=strlen(str);
  if(flg==1)
    for(i=0;i<N;i++)
      s[i]=(int)str[i];
  else 
      s[0]=0;
  return len;
}

Then, in FreeMAT:

--> import('foo.dll','getstr','getstr','int32','int32 flg, int32 N, int32[N] &s');
--> d=0; % dummy variable
--> len=getstr(0,1,d); % Note: here, the 2nd argument should be one
--> d=zeros(1,len);
--> getstr(1,len,d);
--> e=eval(char(d))

e =
 [test1]
 [test23]

 --> e{1}
 ans =
 test1
 --> e{2}
 ans =
 test23
 -->

6 - Use Of Shared Memory - Dynamically Allocated Matrix Data Calling From C (tentative)

Here is a complete example. In this example, a double-type matrix can be dynamically loaded into the FreeMAT.

The C source code:

double **a; // The matrix data
int nRows=0,nCols=0; // Variable numbers of rows and columns for a[][]
char sMessage[2048]; // For showing a message on FreeMAT console

__PORT int GetNRows(void){return nRows;}
__PORT int GetNCols(void){return nCols;}
__PORT int AddData(int N,double __fastcall *b){
  int i;
  if(nRows==0){
    nCols=N;
    a=(double **)calloc(1,sizeof(double *));
    a[0]=(double *)calloc(N,sizeof(double));
    for(i=0;i<N;i++)
      a[0][i]=b[i];
    nRows++;
  }else{
    if(N!=nCols){
      sprintf(sMessage,
        "Input number of cols %d is different from the original %d!!\n",N,nCols);
      FreeMatText(sMessage);
      return -1;
    }
    a=realloc(a,(nRows+1)*sizeof(double *));
    a[nRows]=calloc(N,sizeof(double));
    for(i=0;i<N;i++)
      a[nRows][i]=b[i];
    nRows++;
  }
  return GetNRows();
}
__PORT void PrintData(void){
  int i,j;
  for(i=0;i<nRows;i++){
    for(j=0;j<nCols;j++){
      sprintf(sMessage,"%d ",a[i][j]);
      FreeMatText(sMessage);
    }
    sprintf(sMessage,"\n");
    FreeMatText(sMessage);
  }
}
__PORT int GetData(int N, int M, double __fastcall *b){
  int i,j,n=N;
  if(M!=nCols){
    sprintf(sMessage,"Input number of cols must be the same as %d!!\n",nCols);
    FreeMatText(sMessage);
    return -1;
  }
  if(N>nRows)
    n=nRows;
  for(i=0;i<n;i++)
    for(j=0;j<M;j++)
      b[j*n+i]=a[i][j]; // somewhat must be stored in this order...
  return GetNRows();
}
__PORT void FreeData(void){
  int i;
  for(i=0;i<nRows;i++)
    free(a[i]);
  free(a);
  nRows=nCols=0;
}

Notes

test2.m:

if exist('AddData')==0
  sDll='foo.dll';
  % The import() below must not be called twice, unless 'foo.dll' is unlocked.
  % Otherwise, FreeMAT will crash...
  import(sDll,'AddData','AddData','int32','int32 N, double[N] dat');
  import(sDll,'GetData','GetData','int32','int32 N, int32 M, double[N*M] &dat');
  import(sDll,'GetNRows','GetNRows','int32','');
  import(sDll,'GetNCols','GetNCols','int32','');
  import(sDll,'PrintData','PrintData','void','');
  import(sDll,'FreeData','FreeData','void','');
end

fprintf('First of all, three rows are added into the shared memory');
AddData(3,[1.2 2.3 3.4]);
AddData(3,[4.5 5.6 6.7]);
AddData(3,[7.8 8.9 9.0]);
r=GetNRows(); c=GetNCols();
dat=zeros(r,c);
GetData(r,c,dat);dat

fprintf('Then, another row is added into the shared memory');
AddData(3,[3.2 2.1 1.0]);
r=GetNRows(); c=GetNCols();
dat=zeros(r,c);
GetData(r,c,dat);dat

FreeData(); % Call this, when 'dat' is no more needed.

Then, on FreeMAT console, execute it:

--> test2
First of all, three rows are added into the shared memory
ans =
    1.2000    2.3000    3.4000
    4.5000    5.6000    6.7000
    7.8000    8.9000    9.0000

Then, another row is added into the shared memory
ans =
    1.2000    2.3000    3.4000
    4.5000    5.6000    6.7000
    7.8000    8.9000    9.0000
    3.2000    2.1000    1.0000

-->

References

1. On-line manual of FreeMAT(v4.0) - import command


Written by Tetsuya Hoya