18.10 Details on DLLs


18.10.1 General

A DLL written in C or C++ can contain any sort of functions written in these languages. Note that the parameters of a C or C++ function are called by reference and that the function return value is an integer. Thus, your function head has to be of the following type

EXTERN int EXPORT MyFunction(double *arg1, double *arg2, ....)


Note:


18.10.2 dll.h

The file dll.h is a header file which ensures that your C program is compatible with all the mentioned compilers. You may put dll.h into your directory where you have your C/C++ sources.


Let us shortly examine the include file dll.h:

#ifdef __cplusplus
  #define EXTERN extern "C"
#else
  #define EXTERN
#endif

/**** Compiler depended definitions ****/
/**** Definition of EXPORT */
/**** Definition of nan, inf and math constants */
/**** Definition of fixed byte datatypes */


/**** GNU C++  */

#ifdef __GNUC__

  #define EXPORT

  double dnan = 0.0/0.0, dinf = 1.0/0.0;

  #define INT1  signed char
  #define INT2  short
  #define INT4  int
  #define INT8  long
  #define DBL4  float
  #define DBL8  double

#endif

/**** Visual C++  */
#ifdef _MSC_VER

  #define EXPORT __declspec(dllexport)

  #include <ymath.h>
  double dnan = _Nan._D, dinf = _Inf._D;
  #define M_PI        3.14159265358979323846

  #define INT1  __int8
  #define INT2  __int16
  #define INT4  __int32
  #define INT8  __int64
  #define DBL4  float
  #define DBL8  double

#endif

/**** Borland C++  */
#ifdef __BORLANDC__

  #define EXPORT __export

  double dnan = 0.0/0.0, dinf = 1.0/0.0;

  #define INT1  __int8
  #define INT2  __int16
  #define INT4  __int32
  #define INT8  __int64
  #define DBL4  float
  #define DBL8  double

#endif

#ifdef __SC__

  #define EXPORT __export

  #include <fp.h>
  double dnan = NAN, dinf = INFINITY;

  #define INT1  char
  #define INT2  short
  #define INT4  long
  // #define INT8       not supported by Symantec :(
  #define DBL4  float
  #define DBL8  double

#endif

/**** If none applies then set some defaults values */

#ifndef EXPORT

  #define EXPORT

  #define INT1  signed char
  #define INT2  short
  #define INT4  int
  #define INT8  long
  #define DBL4  float
  #define DBL8  double

#endif

typedef void* voidp;


The first lines of the include file define the macro EXTERN. Every C/C++ compiler should define a macro called __cplusplus if the code is compiled as C++. If it is compiled as C this macro should not be defined.


The reason to define EXTERN as extern "C" is that C++ uses different internal names for the functions. E.g. if you compile the introductory example under Symantec C++ you will get a function name like ?sum@@YAHPAN00@Z which is not easy to remember.


Then we start with the compiler dependend definitions. For example the EXPORT macro allows to export a function from a DLL. Under Unix (Gnu C/C++) you do not need to declare but under Windows you may have private routines. Every C/C++ compiler generates a unique macro e.g. __GNUC__ the GNU C/C++ compiler or __BORLANDC__ for the Borland C/C++ compiler. This macro is used to set the macro EXPORT correctly such that this function will be really exported. An alternative way under Windows would be to define a vecsum.def file, please have a look at your compiler manual.


Furthermore some mathematical constants and values are defined as well as the data types DBL8 and so on.


If you are using other compilers you are welcomed to add them in
dll.h .


18.10.3 Checking for exportable functions

Once you have compiled a DLL you might want to check which are the exportable functions and how they are named.


Under Windows you can use the Explorer:

  1. go on vecsum.dll
  2. use the right mouse button to get the context menu
  3. choose quick view (under Windows 95/98 you might have to download and to install the quick view from Microsoft)
  4. under the section Export Table you will find the exported functions


Under Unix you might use the tool nm. Just do nm vecsum.so which will show all accessible functions from your shared library.


18.10.4 Which compiler

If you have already an compiler then you should use it. If not, we can give some non-objective results of our trials:


Compiler Size of the vecsum DLL
GNU C/C++ on SUN Solaris 4 KB
GNU C/C++ on Linux 3 KB
Borland C++ 5.02 48 KB
Visual C++ 5.0 143 KB
Symantec C++ 7.0 30 KB
F77 on SUN Solaris 4 KB
G77 on Linux 3 KB
Absoft Pro Fortran 6.0 4 KB


18.10.5 A second example in C

Let us consider a second example. This example implements the random number generator RANDU for uniform random numbers. (You should not use this generator in practice! We will study the problems with this generator below!)

The file randu.c consists of the following C code:

    #include <math.h>
    #include "dll.h"

    /*
      The RANDU random number generator. Do not use this function
      for generating random numbers!! Already in three dimensions,
      points obtained from this generator are located on hyperplanes.

      double *x    : vector, contains random numbers after execution
      double *n    : scalar, length of x
      double *seed : scalar, seed of generator
    */

    EXTERN int EXPORT randu (double *x, double *n, double *seed)
    {
      long i, nn= (long) *n;
      double a = 65539.0, m = 2147483648.0;/* 2^31 */
      printf("Calling randu ...\n");/* This output goes to the shell! */

      *x = *seed;
      for (i=1; i<nn; i++)
       *(x+i)  = fmod(a * *(x+i-1),m);
      *seed=fmod (a * *(x+nn-1),m);
      for (i=0; i<nn; i++)
       *(x+i)  = *(x+i)/m;

      return (0);
    }


You can call this generator with the following XploRe program testrandu.xpl :

       dlclose()
       dlopen("randu.so")    ; or dlopen("randu.dll") under Windows
    ;
       x=matrix(3,500)
       dlcall("randu",x,prod(dim(x)),1234567890)
    ;
       func("plot")
       plot(x')


The first line containing 42402 dlclose closes all open DLLs. This is included for the case that you want you run the macro more than once. The 42405 dlopen command opens the DLL randu.so (Linux) (or randu.dll under Windows).


Next, we create a matrix x of dimension 3 by 500. The resulting uniform numbers will be stored in x. Note that the x here is a matrix whereas the randu C function is written for a vector. This is possible because all XploRe matrices and arrays are columnwise stored in memory. So in fact, the function randu is applied to the vectorized x.


For the seed of the generator, the value 1234567890 is chosen. Finally we plot the colums of x. By rotating the point cloud you can easily find a projection where all columns of x are situated on only a few hyperplanes.