A declaration of the form
:- pragma foreign_proc("Lang", Pred(Var1::Mode1, Var2::Mode2, …), Attributes, Foreign_Code).
or
:- pragma foreign_proc("Lang", Func(Var1::Mode1, Var2::Mode2, …) = (Var::Mode), Attributes, Foreign_Code).
means that any calls to the specified mode of Pred or Func will result in execution of the foreign code given in Foreign_Code written in language Lang, if Lang is selected as the foreign language code by this implementation. See the “Foreign Language Interface” chapter of the Mercury User’s Guide, for more information about how the implementation selects the appropriate ‘foreign_proc’ to use.
The foreign code fragment may refer to the specified variables (Var1, Var2, …, and Var) directly by name. It is an error for a variable to occur more than once in the argument list. These variables will have foreign language types corresponding to their Mercury types, as determined by language and implementation specific rules.
All ‘foreign_proc’ implementations are assumed to be impure. If they are actually pure or semipure, they must be explicitly promised as such by the user (either by using foreign language attributes specified below, or a ‘promise_pure’ or ‘promise_semipure’ pragma as specified in Impurity).
Additional restrictions on the foreign language interface code depend on the foreign language and compilation options. For more information, including the list of supported foreign languages and the strings used to identify them, see the language specific information in the “Foreign Language Interface” chapter of the Mercury User’s Guide.
If there is a pragma foreign_proc
declaration
for any mode of a predicate or function,
then there must be either a clause or a pragma foreign_proc
declaration
for every mode of that predicate or function.
Here is an example of code using ‘pragma foreign_proc’. The following code defines a Mercury function ‘sin/1’ which calls the C function ‘sin()’ of the same name.
:- func sin(float) = float. :- pragma foreign_proc("C", sin(X::in) = (Sin::out), [promise_pure, may_call_mercury], " Sin = sin(X); ").
If the foreign language code does not recursively invoke Mercury code, as in the above example, then you can use ‘will_not_call_mercury’ in place of ‘may_call_mercury’ in the declarations above. This allows the compiler to use a slightly more efficient calling convention. (If you use this form, and the foreign code does invoke Mercury code, then the behaviour is undefined — your program may misbehave or crash.)
If there are both Mercury definitions and foreign_proc definitions for a procedure and/or foreign_proc definitions for different languages, it is implementation-defined which definition is used.
For pure and semipure procedures, the declarative semantics of the foreign_proc definitions must be the same as that of the Mercury code. The only thing that is allowed to differ is the efficiency (including the possibility of non-termination) and the order of solutions.
It is an error for a procedure with a ‘pragma foreign_proc’ declaration
to have a determinism of multi
or nondet
.
Since foreign_procs with the determinism multi
or nondet
cannot be defined directly,
procedures with those determinisms
that require foreign code in their implementation
must be defined using a combination
of Mercury clauses and (semi)deterministic foreign_procs.
The following implementation for the standard library predicate
‘string.append/3’ in the mode ‘append(out, out, in) is multi’
illustrates this technique:
:- pred append(string, string, string). :- mode append(out, out, in) is multi. append(S1, S2, S3) :- S3Len = string.length(S3), append_2(0, S3Len, S1, S2, S3). :- pred append_2(int::in, int::in, string::out, string::out, string::in) is multi. append_2(NextS1Len, S3Len, S1, S2, S3) :- ( NextS1Len = S3Len -> append_3(NextS1Len, S3Len, S1, S2, S3) ; ( append_3(NextS1Len, S3Len, S1, S2, S3) ; append_2(NextS1Len + 1, S3Len, S1, S2, S3) ) ). :- pred append_3(int::in, int::in, string::out, string::out, string::in) is det. :- pragma foreign_proc("C", append_3(S1Len::in, S3Len::in, S1::out, S2::out, S3::in), [will_not_call_mercury, promise_pure], " S1 = allocate_string(S1Len); /* Allocate a new string of length S1Len */ memcpy(S1, S3, S1Len); S1[S1Len] = '\\0'; S2 = allocate_string(S2, S3Len - S1Len); strcpy(S2, S3Len + S1Len); ").