[mercury-users] C FFI for discriminated union types.

Julien Fischer juliensf at csse.unimelb.edu.au
Sun Jun 19 23:26:59 AEST 2011


On Sun, 19 Jun 2011, Guillaume Yziquel wrote:

> Le Sunday 19 Jun 2011 à 21:53:29 (+1000), Julien Fischer a écrit :

>> On Sun, 19 Jun 2011, Guillaume Yziquel wrote:
>>
>>> I am now concerned with the C foreign function interface, most notably
>>> concerning discriminated union types. I currently have the following
>>> type declaration:
>>>
>>> 	:- type term_def --->
>>> 		term_def(term)		;
>>> 		term_def_string(str)	.
>>>
>>> And I am passing back values of this type to C code, and would ideally
>>> like to map them to an OCaml algebraic datatype. So I'd like to do the
>>> conversion in my C stub code.
>>>
>>> However, everything seems to be an MR_Word, and there isn't much
>>> structural type information for such discriminated datatypes available
>>> on the C side. So how should I deconstruct them? The reference
>>> guide is fairly
>>> explicit for C# and such, but not for C...
>>
>> The data representation used for (non-enum) discriminated union types in
>> the C grades is lower level that used in the non-C grades and is not
>> very amenable to being manipulated in handwritten C code.  I suggest
>> you consider doing at least some of the conversion in Mercury code.
>>
>> Julien.
>
> Thank you for your answer.
>
> Doing some of the conversion in Mercury code means possibly triggering
> the OCaml GC, which compacts the heap, when doing the conversion from
> Mercury code.
>
> This is possible.
>
> However the tradeoff is:
>
> -1- Do the conversion in Mercury code, and take extra care for OCaml's
> GC in every C stub that calls Mercury since it may trigger OCaml's GC.
>
> -2- Do the conversion in C code, no OCaml GC worries (or at least not
> too much) in exchange for tedious conversion function in C level.

There is a third possibility, do the conversion in C code that makes
calls back to Mercury, via pragma foreign_export, in order to manipulate
the d.u. type.  For the type above, since presumably all you need to do
is deconstruct it, is:

    :- pragma foreign_export("C", is_term_def(in, out), "C_is_term_def").
    :- pred is_term_def(term_def::in, term::out) is semidet.
    is_term_def(term_def(Term), Term).

    :- pragma foreign_export("C", is_term_def_str(in, out),
      "C_is_term_def").
    :- pred is_term_def_string(term_def::in, str::out) is semidet.
    is_term_def_str(term_def_string(Str), Str).

The C code code that converts the Mercury values to OCaml values would
look something like this:

     MR_Word TermDef;
     <Term>  term;
     <Str>   str;

     if (C_is_term_def(TermDef, &term)) {
 	/* Convert term_def/1 with argument term to OCaml */
     } else if (C_is_term_def_string(TermDef, &str)) {
 	/* Convert term_def_string/1 with argument str to OCaml */
     } else {
 	/* error */
     }

(<Term> and <Str> are whatever the C types of term/0 and str/0 are.)

> Both are workable, but I'd appreciate having more background, if only
> informally, about the design of data representation for discriminated
> union datatypes in C grades.

See section 3.2 of the following paper:

     The execution algorithm of Mercury: an efficient purely declarative
     logic programming language
     Zoltan Somogyi, Fergus Henderson and Thomas Conway. JLP, 1996.

which is available from the papers section of the Mercury website, at
<http://www.mercury.csse.unimelb.edu.au/information/papers.html#jlp>.

Note that some of the implementation details have changed since that
paper was written but the basic ideas are pretty much the same.

Julien.


More information about the users mailing list