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:
EXTERN int EXPORT sum (double *x, double *n, double *s)
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.
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:
Under Unix you might use the tool nm. Just do nm vecsum.so which will show all accessible functions from your shared library.
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 |
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
dlclose
closes all open DLLs. This is included for the case, that you want you run the
macro more than once. The
dlopen
command then 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.
![]() |
MD*TECH Method and Data Technologies |
http://www.mdtech.de mdtech@mdtech.de |