Interfacing GNU Modula-2 to C#
The GNU Modula-2 compiler tries to use the C calling convention
wherever possible however some parameters have no C equivalent and
thus a language specific method is used. For example unbounded arrays
are passed as a struct {void *address, unsigned int high}
and
the contents of these arrays are copied by callee functions when they
are declared as non VAR
parameters. The VAR
equivalent
unbounded array parameters need no copy, but still use the
struct
representation.
The recommended method of interfacing GNU Modula-2 to C is by telling
the definition module that the implementation is in the C language.
This is achieved by using the tokens DEFINITION MODULE FOR "C"
.
Here is an example libprintf.def
.
DEFINITION MODULE FOR "C" libprintf ;
EXPORT UNQUALIFIED printf ;
PROCEDURE printf (a: ARRAY OF CHAR; ...) : [ INTEGER ] ;
END libprintf.
the UNQUALIFIED
keyword in the definition module informs
GNU Modula-2 not to prefix the module name to exported references
in the object file.
The printf
declaration states that the first parameter
semantically matches ARRAY OF CHAR
but since the module is for
the C language it will be mapped onto char *
. The token
...
indicates a variable number of arguments (varargs) and all
parameters passed here are mapped onto their C equivalents. Arrays and
constant strings are passed as pointers. Lastly [ INTEGER ]
states that the caller can ignore the function return result if desired.
The hello world program can be rewritten as:
MODULE hello ;
FROM libprintf IMPORT printf ;
BEGIN
printf("hello world\n")
END hello.
and it can be compiled by:
gm2 -g -I. hello.mod -lc
In reality the -lc
is redundant as libc is always included in the
linking process. It is shown here to emphasize that the C library or
object file containing printf
must be present.
If a procedure function is declared using varargs then some parameter values are converted. The table below summarises the default conversions and default types used.
Actual Parameter | Default conversion | Type of actual
| | value passed
===============================================================
123 | none | long long int
"hello world" | none | const char *
a: ARRAY OF CHAR | ADR(a) | char *
a: ARRAY [0..5] OF CHAR| ADR(a) | char *
3.14 | none | long double
If you wish to pass int
values then you should explicitly
convert the constants using one of the conversion mechanisms.
For example: INTEGER(10)
or VAL(INTEGER, 10)
or
CAST(INTEGER, 10)
.