Previous
Home
Contents
Next
Chapter 19: Using Aldor with C
19.1 Using C code from Aldor
19.2 Using Aldor code from C
19.3 Data correspondence
Functions and data structures may be shared between programs written in Aldor and other languages. Here we give simple examples of sharing functions in a mixed Aldor and C programming environment.
Aldor has types corresponding to the primitive C types.
These will be described in section 19.3.
19.1 : Using C code from Aldor
For the first example, we show how to call a C function from Aldor. The Aldor file ``arigato.as'' refers to the function ``nputs'', supplied by the C file ``nputs.c''. Figure 1 shows these files.
The commands to compile these two files and link them together, say on Unix, are:
% cc -c nputs.c % aldor -Fx arigato.as nputs.o % arigato Arigato gozaimasu! Arigato gozaimasu! Arigato gozaimasu!
The first command produces the object file ``nputs.o''. The second command compiles the file ``arigato.as'' and links it with our other file to form an executable program. Finally, the third command runs the resulting program.
The Aldor compiler can make use of C-generated object files, whether they are kept loose or packaged with others in a library archive. The Aldor file using the code or data from C must declare it with the statement ``import ... from Foreign C'': this is the purpose of this statement in ```arigato.as''. When a function is imported from C, a declaration is placed at the head of the generated C file. This declaration is constructed using the data correspondence below.
To call a C function or macro (such as ``fputc'') defined in a header file (such as ``<stdio.h>'' or ``myfile.h'') an import of the following form should be used:
import { fputc: (SingleInteger, OutFile) -> SingleInteger }
from Foreign C "<stdio.h>";
import { myfun: SingleInteger -> () }
from Foreign C "myfile.h";
The filename indicates the file to include when generating C. No
declaration for ``fputc'' or ``myfun'' is produced in the generated C
-- it is assumed that all the imports are declared
(or, in the case of macros, defined).
A ``#include'' line is produced for every header file mentioned in
the source code, even when no imported function is used. This allows
some of the definitions in ``foam_c.h'' to be over-ridden. For
example, one could replace the memory management primitives with
operations specifically optimized for the current
application.
Figure 19.1: Aldor code using a C function 19.2 : Using Aldor code from C
--
-- arigato.as: A main Aldor program calling the C function 'nputs'.
--
#include "aldor.as"
SI ==> SingleInteger;
import { nputs: (SI, String) -> SI } from Foreign C;
import from SI;
nputs(3, "Arigato gozaimasu!");
/*
* nputs.c: A simple C function.
*/
void
nputs(int n, char *s)
{
int i;
for (i = 0; i < n; i++) puts(s);
}
Figure 19.2: C code using Aldor function.
/*
* cside.c: A main C program calling the A# function 'lcm'.
*/
#include "foam_c.h"
extern FiSInt lcm (FiSInt, FiSInt);
int
main()
{
printf("The lcm of 6 and 4 is %d\n", lcm(6,4));
return 0;
}
--
-- aside.as: An Aldor function made available to C.
--
#include "aldor.as"
SI ==> SingleInteger;
export { lcm: (SI, SI) -> SI } to Foreign C;
lcm(n: SI, m: SI): SI == (n quo gcd(n,m)) * m;
For the second example, we show how to call an Aldor function from C.
C code which uses Aldor functions should include the file
``foam_c.h''. This file contains C type definitions
corresponding to the various Aldor primitive types.
For example, ``FiSInt'' is a typedef for the C type corresponding
to ``SingleInteger''.
On Unix, the full path name for this file is
``$ALDORROOT/include/foam_c.h''.
For this example, the C file ``cside.c'' refers to the function ``lcm'', supplied by the Aldor file ``aside.as''. These files are shown in figure 19.2.
The commands to compile, link, and run these files are:
% aldor -Fo aside.as % cc -I$ALDORROOT/include -c cside.c % cc cside.o aside.o -o lcm64 -L$ALDORROOT/lib -laldor -lfoam -lm % lcm64 The lcm of 6 and 4 is 12
The first command compiles the Aldor code in the normal way to produce an object file. On Unix, this produces the object file ``aside.o''.
Compiling the C code which uses ``aside.o'' requires the use of a ``-I'' option to tell the compiler where to find ``foam_c.h''.
Additional options are needed to link an executable program: a ``-L'' option tells the C compiler where to look for libraries, and the ``-l'' options list the libraries which provide Aldor support functions.
The ``-laldor'' option provides a library with basic Aldor types such as floating point numbers, lists, file I/O, and so on.
The ``-lfoam'' option provides a library with run-time support for such things as memory management and big integer arithmetic. Applications can supply their own run time support library instead, if desired. Basically, the idea is to provide alternative macro definitions in ``foam_c.h'' and a C file with whatever code is needed by the macros.
The -lm option makes the standard C math library available. Because of the way Aldor compiles domains, this generally needs to be included even if no operations from the math library are used.
In ``aside.as'', the line beginning ``export to'' tells the
export
compiler that a wrapper function called lcm should be generated
for the Aldor function with the same name. This wrapper will
convert the C calling convention into that used by
Aldor using the rules in the next section. Currently it is
possible to export only functions in this way (an Aldor constant
can be wrapped in a function, and types have no particular use in C).
19.3 : Data correspondence
This section describes the correspondence between the way data values are represented in Aldor and the way they are represented in C. It should be possible from this to understand which Aldor declaration will correspond to a declaration in C, and vice versa.
Aldor's abstract machine defines a number of types which correspond to types on the target machine (in this case C on top of some operating system). The ``Machine'' package, described in section 13.16, exports the types provided by the abstract machine. All Aldor values are represented internally as elements of one of these types. The complete listing and definition of the types is given in the FOAM reference guide.
Because many Aldor domains can be parameterized over different types, Aldor uses a pointer-sized object when passing domains. Thus, double precision floating point numbers (which are typically bigger than pointers) are ``boxed'', and a pointer to the box is passed, rather than the number itself. Types which are the same size or smaller than pointer-size are cast to the pointer type when used in a generic context and cast back as appropriate.
In order to make the underlying types available, Aldor provides the ``Machine'' package, which exports these types and operations on them. For example, the underlying representation type of DoubleFloat is DFlo$Machine. This type should be used when calling foreign functions, and the result coerced back to appropriate generic type at the Aldor level.
Records in Aldor are represented by an aggregate type of some kind in the hosting language. For example, in Scheme a vector is used (and all objects are the same size anyway). In C, structures are used. When calling C-defined functions that use records it is important to ensure that the elements of the Aldor record correspond to elements in the C structure. This implies that records intended for use in Foreign functions should use the underlying types, rather than the user-level types.
The Aldor types correspond to C types given as typedefs in the file ``$ALDORROOT/include/foam_c.h''. The following table shows the correspondance between the types exported from the Aldor package ``Machine'' and C:
| Aldor type | C typedef | Usual C type |
Nil$Machine | FiNil | Ptr |
Word$Machine | FiWord | int |
Arb$Machine | FiArb | long int |
Ptr$Machine |
FiPtr |
Ptr |
Bool$Machine | FiBool | char |
Byte$Machine | FiByte | char |
HInt$Machine | FiHInt | short |
SInt$Machine | FiSInt | long |
Char$Machine | FiChar | char |
Arr$Machine | FiArr | Ptr |
Rec$Machine | FiRec | Ptr |
BInt$Machine | FiBInt | Ptr |
SFlo$Machine | FiSFlo | float |
DFlo$Machine | FiDFlo | double |
A -> B | FiClos | struct _FiClos * |
Here ``Ptr'' is defined as the type ``char *'' for compatibility with old C dialects, but could equally well be defined as ``void *''.
All other user defined types in Aldor correspond to the C linebreak
typedef
``FiWord''. This includes Integer, SingleInteger and so
on. The data correspondence on most 32 bit machines allows one to
treat SingleInteger and SInt$Machine as the same type
(which is the reason that arigato, above, works). However, on
other machines, for example 16 or 64 bit machines, the two types are
not equivalent.
Values belonging to "Record" types, are pointers to C structs of the corresponding members. For example, the C declaration
struct {
int x;
short y;
double z;
} *r;
corresponds to the Aldor declaration
local r: Record(
x: SInt$Machine, -- or x: SingleInteger
y: HInt$Machine,
z: DFlo$Machine
);
Functions which return no value, or more than one value, are declared to be of
type void, and additional return results are returned through pointers passed in as additional arguments.Thus the expression:
import {
foo: (Integer, Integer) -> (SInt$Machine, BInt$Machine)
} from Foreign C;
implies that foo should be declared (in ANSI C) as:static void foo(FiWord, FiWord, FiSInt *, FiBInt *);
A number of examples of exporting and importing C-defined functions can be found in ``$ALDORROOT/samples/test''.
Previous
Home
Contents
Next