(j3.2006) (SC22WG5.3714) the interoperability TR - an alternative descriptor design

N.M. Maclaren nmm1
Wed Dec 3 13:33:42 EST 2008


This has been written in haste, as I am away for a month from next
Tuesday, and have other work to do!  It isn't finished enough for a
proposal, but may indicate how I think this should be done.  Note that I
have implemented something fairly similar (for Algol 68 and Fortran 77),
and have seen a fair number of similar designs.



C Interoperability -  an Alternative Design
-------------------------------------------

Note that this is NOT addressing the syntactic extensions proposed in
N1761 (i.e. TYPE(*) or DIMENSION(..)), but the actual way that the
procedure interface to C is specified.  The main point of this is to
decouple the implementation from the specification.

In particular, it is negligibly more expensive to implement that the
mechanism proposed on N1761 and supports all of omitted features I
mentioned above.  The intention is that it should be potentially
extensible to supporting all procedure calls that could reasonably be
defined to be interoperable.

The only syntactic change is that, for procedures, we extend the BIND
attribute:

proc-language-binding-spec is
    BIND(C[,METHOD=binding_method][NAME=scalar-char-initialization-expr])

binding_method is DIRECT
               or DESCRIPTOR

The default is DIRECT, and is what we have at present.


For DESCRIPTOR, a procedure called from Fortran is invoked in a processor
dependent fashion.  If the calling sequences are compatible, it might
be called directly; if not, it would be called via a thunking mechanism
written in assembler.

When the call reaches C, it has the following prototype:

    void function (const CFI_interface *arguments);

Such a function (whether in Fortran or C) is called from C by a
processor-provided macro which has a prototype:

    void CFI_invoke (void (*function)(const CFI_interface *), 
        const CFI_interface *arguments);

The arguments include both the result type and the arguments proper,
because the C result mechanism is too limited for all of Fortran's
features.


CFI_interface is a recursive definition, exactly as in Fortran, and so
allows any type that can be defined in Fortran.  The next section
contains a VERY rough draft of a possible specification, described
entirely in terms of C; I regret that I do not have time to do better
just now.




A Possible Interface Type
-------------------------

The header is defined as unitary, and all names beginning CFI_ are
reserved.

I describe it in terms of the structures that C will see when DESCRIPTOR
is specified.  Note that the actual header will need to use structure
and union tags, because C does not support recursive definitions.  Also,
I have used simple names for fields, for clarity, but they would need
prefixing by CFI_ to avoid namespace pollution.

This proposal includes the field names in argument lists and derived
types.  C can't make use of them, though some other languages can, but
the real point they are included is for debuggers as companion
processors.

Note that the attributes are flags - i.e. bits in a mask (with INTENT
taking two), and would include flags other than attributes per se.  I
have allowed for 16 properties per procedure, and 32 'attributes'.

Note that the totality of an interface descriptor is constant, and only
the data are updatable.  Simple uses do not need updatable descriptors,
and they are inadequate for complicated ones (e.g. where subcomponents
of an argument are copied and changed separately, or if it were extended
to support CLASS).  In particular, it can be compiled and placed in
read-only memory for MODULEs and COMMON - and that is a useful facility.

The uintptr_t fields are opaque, and are passed to the auxiliary
procedures so that they can access Fortran's internal structures (as is
needed for CFI_allocate, CFI_deallocate etc.)  The 'pointer' field is
zero for arguments that are not ALLOCATABLE or POINTER.

    typedef struct {
        uint_least16_t flags;            /* PURE, RECURSIVE etc. */
        const CFI_descriptor *result;    /* NULL for a subroutine */
        int arg_count;
        const CFI_descriptor[] arguments;
    } CFI_interface;

    typedef struct {
        const char *name;          /* The name of the argument or field */
        int_least16_t type;        /* Which member of the union */
        int_least16_t subtype;     /* CFI_char, CFI_int etc. */
        uint_least32_t attributes; /* The attributes */
        uintptr_t pointer;         /* Also used for ALLOCATABLE */
        union {
            CFI_intrinsic;
            CFI_array;
            CFI_derived;
            CFI_opaque;            /* I.e. Not interoperable */
            CFI_procedure;
            CFI_class;             /* Currently not proposed */
            . . .
        } type;
    } CFI_descriptor;

    typedef struct {
        int size;                  /* The storage size in bytes */
        void *location;
        size_t length;             /* The length of CHARACTER */
    } CFI_intrinsic;

    typedef struct {
        int dimensions;            /* The number of dimensions */
        void *location;            /* As in N1761 */
        const CFI_descriptor *element;   /* Element type descriptor */
        const ptrdiff_t bounds[][3];     /* The array bounds and stride */
    } CFI_array;

The bounds are triples of the lower bound, upper bound and stride, in
units of the element size.  The upper bound and stride are zero for an
unspecified assumed-shape or deferred-shape array; a stride of zero is
unambiguous.  An assumed-size array could be specified in one of three
ways: by an 'attribute' saying that the argument is assumed-size, by
providing a last upper-bound derived from its storage size, or by adding
an extra ptrdiff_t argument giving its storage size.

    typedef struct {
        int fields;                /* The number of fields */
        void *location;            /* As in N1761 */
        const CFI_descriptor elements[];   /* Element type descriptors */
    } CFI_derived;

    typedef struct {
        uintptr_t token;           /* Copyable, but nothing else */
    } CFI_opaque;

    typedef CFI_interface CFI_procedure;

All of the locations are NULL in the arguments of procedure arguments,
of course, and the strides in arrays and length in character may be
zero.  More work is needed on the precise specification.



Regards,
Nick Maclaren,
University of Cambridge Computing Service,
New Museums Site, Pembroke Street, Cambridge CB2 3QH, England.
Email:  nmm1 at cam.ac.uk
Tel.:  +44 1223 334761    Fax:  +44 1223 334679




More information about the J3 mailing list