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).