18.9 A complex example in C


18.9.1 dlcallex

Sometimes we need to give different type of parameters to our C/C++ functions e.g. we may need more then 12 parameters or we are using code from someone else who requires some integer parameters. This gap is closed by 42079 dlcallex .


The syntax of 42082 dlcallex is:

                err = dlcallex (h, "func", param, opt)


with h a DLL handle, func a DLL function, param a list of an arbitrary number of parameters (only numeric or text !!) and opt an optional list which specifies the elements of param more.


Let us rewrite our slightly modified dlcallex1.xpl such that we use 42087 dlcallex

  h = dlopen("dlcallex1.so")
  x = (1:3)~(4:6)~(7:9)         ; create a 3x3 matrix instead of 10x1
  s = 0
  n = rows(x)
  x
;
  param = list (x, n, s)        ; build parameter list
  byrow = 0|0|0                 ; actually this is the default
  type  = 8|8|8                 ; actually this is the default
  opt   = list (type, byrow)    ; build parameter description list
;
  err = dlcallex(h, "sumd8", param, opt)
  param.s                       ; get the result of 45 = 1+...+9 back


which prints finally the result of 45 in the output window.


18.9.2 An arbitrary number and type of parameters

We see already that the list param could contain an arbitrary number of arguments. Thus the corresponding C program dlcallex1.c has to change too:

#include "dll.h"

EXTERN int EXPORT sumd8 (int argc, int *type, voidp *ptr)
{ long i, nn;
  DBL8 *x, *n, *s;

  x = (DBL8 *) ptr[0];
  n = (DBL8 *) ptr[1];
  s = (DBL8 *) ptr[2];

  nn = (long) *n;
  *s = 0;
  for (i=0; i<nn; i++)
    *s += *(x+i);

  return (0);
}


The first parameter argc tells us how many parameters are given by the user. The second parameter type tells us which type the parameter given to our function sumd8 has. This is identically to the list given in opt.type. The following types are possible:


XploRe object C/C++ data type entry in opt.type
numeric 8 byte float 8
  4 byte float 4
numeric 8 byte integer -8
  4 byte integer -4
  2 byte integer -2
  1 byte integer -1
text 1 byte char -1


Note that we can not take any responsibility for a successful conversion. For example the Symantec compiler does not provide a 8 byte integer. Also the conversion for a float value to a 1 byte integer will fail if the float values are outside the range -126 till +127.


The third parameter ptr is a set of pointers on pointers which contain the data from XploRe. Actually we could have used void** but it seems that the Borland compiler does not like such a definition, thus we have defined typedef void* voidp;.


The first step in the program is to assign the elements to the "right" pointers. Since we know we have exactly three double pointers there is no need to check argc or type. The second step is to compute the sum of all values.


Note that we use the macro DBL8 rather than double. DBL8 stands for an 8 byte float value which may not be the double data type in all C/C++ compilers. According to the table before we define also DBL4, INT8, INT4, INT2 and INT1.


Since we are using quite small numbers we could do the same just using 1 byte integers. Here are the according XploRe program dlcallex2.xpl

  h = dlopen("vecsum.so")
  x = (1:3)~(4:6)~(7:9)         ; create a 3x3 matrix instead of 10x1
  s = 0
  n = rows(x)
  x
;
  param = list (x, n, s)        ; build parameter list
  byrow = 0|0|0                 ; actually this is the default
  type  = -1|8|-1
  opt   = list (type, byrow)    ; build parameter description list
;
  err = dlcallex(h, "sumd8", param, opt)
  param.s                       ; get the result of 45 = 1+...+9 back


and the C/C++ program dlcallex2.c

#include "dll.h"

EXTERN int EXPORT sumi1 (int argc, int *type, voidp *ptr)
{ long i, nn;
  DBL8 *n;
  INT1 *x, *s;

  x = (INT1 *) ptr[0];
  n = (DBL8 *) ptr[1];
  s = (INT1 *) ptr[2];

  nn = (long) *n;
  *s = 0;
  for (i=0; i<nn; i++)
    *s += *(x+i);

  return (0);
}


Note that we have used here mixed parameters. The first parameter is 1 byte integer, the second is 8 byte float and the last parameter again 1 byte integer.


18.9.3 Storing by row or by column

At last, let us talk about the setting in opt.byrow. For each parameter it can take the value zero or non-zero. By default the value zero is used which means the data in a matrix are stored columnwise.


Consider the matrix x in XploRe which prints as

Contents of x
 [1,]        1        4        7
 [2,]        2        5        8
 [3,]        3        6        9


The pointer x in dlcallex1.c contains the values

x = {1, 2, 3, 4, 5, 6, 7, 8, 9}


If the entry in opt.byrow is non-zero then the pointer x contains the values

x = {1, 4, 7, 2, 5, 8, 3, 6, 8}


which means the data are stored rowwise.


18.9.4 Test program

To test the functionality of 42234 dlcall and 42237 dlcallex you may use our test programs: dltestex.xpl (XploRe program) and vecsumex.C (C++program).


Especially the last routine sumc1 is interesting since it shows how to transfer a single string.