[m-dev.] for discussion: foreign_export_enum syntax

Jonathan Morgan jonmmorgan at gmail.com
Mon Jul 2 16:36:40 AEST 2007


On 6/27/07, Julien Fischer <juliensf at csse.unimelb.edu.au> wrote:
> I have been working on the change to allow Mercury enumeration constants
> to be exported to foreign languages.  The majority of the changes to
> the compiler were made last year; what remains undecided is the
> concrete syntax.  Below is change to the reference manual that describes
> the current version of this new feature.  Here is a small example of
> how it would look and work in the case where C is the foreign language.

I've been considering bringing this up again in the last couple of
weeks, so you've saved me the trouble of doing that.

> Example
> -------
>
> :- type foo
>                 --->    foo
>                 ;               bar
>                 ;               baz.
>
>         :- pragma foreign_export_enum("C", foo, [bar - "BAR],
>                 [prefix("MR_"]).
>
> would cause the following declaration to be generated in the .mh file.
>
>         enum {
>                         MR_foo = 0,
>                         MR_BAR = 1,
>                         MR_baz = 2
>         };

Is there any particular reason for choosing enums over #define's?  I
would expect #define's to be a better option as they do not specify
any type for the resultant constant, whereas an enum specifies an enum
type for the constant, which is probably different from the standard
Mercury type (MR_Word?).  While this may not be a problem with C's
weak type system, it could potentially cause problems with C++ or
C/C++ compilers (are there any plans to support a C++ compiler with
any of the high-level C grades?).

> (The following diff does not yet contain the language specific information
> for pragma foreign_export_enum.)
>
> Any comments or suggestions welcome.
>
> Julien.
>
> Index: reference_manual.texi
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
> retrieving revision 1.397
> diff -u -r1.397 reference_manual.texi
> --- reference_manual.texi       12 Jun 2007 06:53:57 -0000      1.397
> +++ reference_manual.texi       27 Jun 2007 08:44:15 -0000
> @@ -6176,7 +6176,10 @@
>                                          programming language.
>   * Using foreign types from Mercury::   How to use a type defined in
>                                        a different programming language
> -                                      in Mercury code.
> +                                      in Mercury code.
> +* Using Mercury enumerations in foreign code:: How to use a enumeration type
> +                                               defined in Mercury in a
> +                                               different programming language.
>   * Data passing conventions::         How Mercury types are passed to
>                                        different languages.
>   * Adding foreign declarations::        How to add declarations of
> @@ -6867,6 +6870,71 @@
>
>   @c -----------------------------------------------------------------------
>
> + at node Using Mercury enumerations in foreign code
> + at section Using Mercury enumerations in foreign code
> +
> +Values of Mercury enumeration types can be made available to code in the
> +bodies of @samp{foreign_proc} and @samp{foreign_code} pragmas via
> +a declaration of the form:
> +
> + at example
> +:- pragma foreign_export_enum(@var{Lang}, @var{MercuryType},
> +        @var{Overrides}, @var{Attributes}).
> + at end example
> +
> +This causes the compiler to create a symbolic name in language
> + at var{Lang} for each of the constructors of @var{MercuryType}.
> +The symbolic name allows the foreign code to create a value
> +corresponding to that of the constructor it represents.
> +(The exact mechanism used depends upon the foreign language;
> +see the language specific information below for further details.)

I like the idea of having a default name.

> +For each foreign language there is a default mapping between the name
> +of a Mercury constructor and its symbolic name in the language @var{Lang}.
> +This default mapping is not required to map every valid constructor name
> +to a valid name in language @var{Lang}; where it does not the programmer
> +must specify a valid symbolic name.
> +The programmer may also choose to map a constructor to a symbolic name
> +that differs from the one supplied by the default mapping for language
> + at var{Lang}.
> + at var{Overrides} is a list of pairs of constructor names and strings of
> +the following form:
> +
> + at example
> +[consI - "symbolI", ..., consJ - "symbolJ"]
> + at end example
> +
> +This can be used to provide either a valid symbolic name where the
> +default mapping does not, or to override a valid symbolic name
> +generated by the default mapping.

Do you have to specify an empty list if there are no renamings? (I can
imagine that requirement being potentially confusing if most uses of
the pragma didn't do any renaming).  An alternative (for if renaming
was scarce) would be to make each renaming a separate attribute (e.g.
rename(x, "X")), though I don't think that it would really help.

> +The argument @var{Attributes} is a list of optional attributes.
> +If empty, it may be omitted from the @samp{pragma foreign_export_enum}
> +declaration.
> +The following attribute must be supported by all Mercury implementations.
> +
> + at table @asis
> +
> + at item @samp{prefix(Prefix)}
> +Prefix each symbolic name, regardless of how it was generated, with
> +the string @var{Prefix}.
> +This occurs @emph{before} the validity of the symbolic name in the
> +foreign language is checked, i.e. the effect of the @samp{prefix}
> +attribute may cause an otherwise valid symbolic name to become invalid or
> +vice versa.

If it occurred before the validity check is done, shouldn't its
validity also be checked? (and thus it shouldn't cause the name to be
invalid)

Is there a requirement that there is only one prefix, or can I have
multiple prefixes?

> + at end table
> +
> +It is an error for a single constructor to be mapped to two different symbolic
> +names by a single @samp{pragma foreign_export_enum} declaration.
> +A program can contain multiple @samp{pragma foreign_export_enum}
> +declarations for a single Mercury type.
> +
> +A module may contain @samp{pragma foreign_export_enum} declarations that
> +refer to imported types, subject to the usual visibility restrictions.
> +
> + at c -----------------------------------------------------------------------
> +
>   @node Adding foreign declarations
>   @section Adding foreign declarations

How are single-value enumerations handled?  The original version of
this proposal defined an enumeration as requiring at least two
constructors, but I can see no real reason for preventing single-value
enumerations from being exported (if I understand correctly, all the
dummy type optimisation does is to substitute the single acceptable
value as a constant in places where it is needed, so there is a valid
value to be exported).  Just FYI, Gtk+ has a few single value
enumerations.

I've also been considering an implementation for foreign_import_enum,
if we were to have such a thing.  I assume that it would look
something like:

:- pragma foreign_import_enum("C", my_enum, "MyEnum", [a -
"MY_ENUM_A", b - "MY_ENUM_B"]).

[something of a cross between a foreign type declaration and a type
constructor declaration].

and I've come up with the following couple of ideas:

1. Represent the imported type as a foreign type, and provide
functions to access the valid constants.

2. Create a new Mercury type for the imported type, and marshal
between the Mercury type and the foreign type whenever going into or
out of a foreign code layer (e.g. at foreign_proc and foreign_export
boundaries).

While neither of these ideas are necessarily perfect, they each offer
different trade-offs.  The first option is fairly efficient (no
marshalling costs), but potentially requires a function call for
creating any value in Mercury.  The second option allows switching on
the type in Mercury code, but requires conversions on every foreign
language call.  Neither option supports overlapping enumeration values
very well (which happens in quite a few libraries).

A potential problem with this declaration as is is that it doesn't
support a good distinction between whether the constructors for the
type are public or private.  With an ordinary type declaration, this
would be determined by whether the type definition was in the
interface section or the implementation section, but it is not usual
to put foreign declarations of this sort in Mercury interfaces (as
they expose implementation details).  Also, the constants used in the
definition will need to be accessible to the generated code (probably
through foreign_decl clauses).

Jon
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at csse.unimelb.edu.au
Administrative Queries: owner-mercury-developers at csse.unimelb.edu.au
Subscriptions:          mercury-developers-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the developers mailing list