errno again

Fergus Henderson fjh at cs.mu.oz.au
Fri Dec 1 21:13:08 EST 2000


On 01-Dec-2000, Marcin 'Qrczak' Kowalczyk <qrczak at knm.org.pl> wrote:
> There is a fundamental difference between functions and variables on
> one side, and macros, enum constants and struct fields on the other.
> 
> Using objects of the first kind does not require cooperation with a C
> compiler.
...
> Current foreign declarations support only the first kind. They are
> implementable in a native code generator. They are even independent
> of the language, provided that names are not mangled, types are
> compatible and the calling convention is supported.
> 
> Approaching the second kind in the core language is tricky. First of
> all, it must be somehow defined which C headers to include to find
> a definition, and what preprocessor symbols to #define to make it
> available.

Mercury supports this using `foreign_decls' and `foreign_code' pragmas.
Originally we just supported C, and so these were named
`c_header_code' and `c_code' respectively, but we're currently in the
process of generalizing this to support arbitrary languages.
The `foreign_decls' pragma lets you include any foreign language
declarations that the foreign language code might need, e.g.

	:- pragma foreign_decls(c, "
		#define _POSIX_SOURCE 1

		#include "conf.h"
		#ifdef HAVE_UNISTD_H
		  #include <unistd.h>
		#else
		  #include <stdio.h>
		#endif

		extern int my_global;
	").

For the `foreign_code' pragma, there are two forms.
The first form just specifies a block of foreign code
that will be compiled with the foreign language compiler
and linked into the program.   E.g.

	:- pragma foreign_code(c, "
		int my_global;
	").

This form is no strictly necessary, since you could always write such
code in the foreign language itself, rather than in Mercury code
inside `pragma foreign_code', but it is often very convenient.

The other form is for when you are implementing a Mercury procedure
using foreign code.  You give the declaration of the Mercury procedure,
e.g.

	:- func sin(float) = float.
	:- mode sin(in) = out is det.	% this line is optional

and then you give a `pragma foreign_code' declaration to implement it:

	:- pragma foreign_code(
		% specify which language
		c,

		% specify which function this is for,
		% and which mode of the function, and
		% also introduce variable names for the arguments
		sin(X::in) = (Y::out), 

		% a list of flags; these can give the Mercury
		% compiler additional information to help it
		% generate more efficient code
		[will_not_call_mercury, thread_safe],

		% The foreign language code to implement the procedure
		% This uses the variable names introduced earlier
		"Y = sin(X);"
	).

For the Mercury -> MS IL back-end (and soon for the Mercury -> Java
back-end), the Mercury compiler extracts the foreign_decl and
foreign_code stuff, wrapping code in the second form of foreign_code
up inside appropriate function definitions, and puts them all in a
separate file that it then invokes the foreign language compiler on.

This does require a bit of language-specific knowledge in the
Mercury compiler, because it needs to know the foreign language
syntax for function definitions, so that it can wrap up the code from
a `foreign_code' declaration in an externally visible function
definition.  Currently we only support C, but adding support for
other languages would not be hard.  For example, for Haskell I suppose
if you wrote

	:- module example.

	...

	:- pragma foreign_decl(haskell, "
	import Prelude		-- this is not a good example ;-)
	").

	:- func haskell_sin(float) = float.
	:- pragma foreign_code(haskell, [],
		haskell_sin(x::in) = (y::out),
		"y = sin x"
	).

we would generate a Haskell file containing something like this:

	import Prelude		-- this is not a good example ;-)

	example__haskell_sin_1_f_0 x = y where
		y = sin x

The name `example__haskell_sin_1_f_0' is a mangled name corresponding
to the name of the Mercury procedure that this code is for; the
"1_f_0" bit means that this is mode number zero of a function with
one argument.

> Don't forget about specifying which C compiler to use and
> with which options, e.g. which include paths.

Those are not part of the language.  Those are specified by options
which you pass to the Mercury compiler, or more likely put in your
makefile.  E.g. to compile the C example you could use

	mmc --cc gcc --cflags "-I/foo/bar" example.m

(this is currently supported) and to compile the Haskell one
you could use

	mmc --haskell-compiler ghc --haskell-flags \
		"-fallow-undecidable-instances" example.m

Or you could put both examples in the same module and
pass both sets of flags to mmc.

> There are two separate issues:
> 
> 1. Is it possible to have the second kind from the level of the
>    Haskell compiler at all (done in an sufficiently elegant way to
>    think about it as a candidate proposal for the official FFI)?

I think so.  I certainly consider the mechanism that Mercury
uses to be elegant enough to be the official Mercury FFI,
and I hope our standards are not so much lower than yours ;-)

> 2. If so, which features are worth supporting?

See above.

Cheers,
	Fergus.

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
                                    |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.




More information about the FFI mailing list