HELP NEWC_DEC

Ian Rogers, June 1990


See HELP * EXTERNAL for basic details of the external interface.

Contents

Select headings to return to index

Accessing these facilities

The facilities described in this file are obtained by adding the line:

uses newexternal;

or

uses newc_dec;

before any attempt is made to parse any C code (ie. before any occurence of the keywords "external declare name in c")

Recognised types

As well as all the types recognised by the old parser (see HELP * EXTERNAL), LIB *NEWC_DEC has some extra capabilities:

"typedef" works, eg:

typedef char *String;
    pr_string(x)
        String x;
        {}

You can parse structs, eg.

struct _two_shorts {short x,y;};

and use them in typedefs:

typedef struct _two_shorts Coord;

or, more normally:

typedef struct _two_shorts {short x,y;} Coord;

Unions are also supported, eg.

    typedef union {int                  type;
                   struct _two_shorts   plane_vertex;
                   struct _three_shorts solid_vertex;}
            AnyVertex;

Enumerated types can be used to introduce a set of defined constants just as in C. Eg:

typedef enum {False, True} Bool;

will create the two pop11 macros -False- and -True- (with the values 0 and 1 respectively). Bool is typed to be an "int" and can be used as an ordinary type specifier.

All about structures

When a structure is declared, a "constructor" procedure for that structure is constructed based on the name of the structure. The name of the procedure is constructed by prefixing the name of the structure with "init". Structures declared by "typedef" are also honoured (in fact this is the normal way of doing things) eg.

uses newexternal;
external declare test in c;
typedef struct _two_shorts {short x,y;} Coord;
endexternal;
initCoord => ** <procedure initCoord>

This procedure takes either false or an external pointer class record (see REF * EXTERNAL). If the argument is an external pointer it is assumed to point to the base address of some structure in external memory. In this case a sufficient amount of data is construct on the Poplog side to "shadow" this structure. This is how you access external structures.

Alternatively, if the argument is false, then a piece of blank external memory is constructed, along with everything else, and it is this piece of memory which is shadowed. This is how you construct structures fit to pass out to C programs.

As record returned by the init procedure is itself an external pointer class record (which points to the base address of the external memory) it can, in turn, be given as an argument to an init procedure of a different struct. This is how "structure shadowing" can be implemented.

As well as the "init" procedure, "cons", "dest" and "is" procedures are also built.

Accessing the values in structure slots is done by two new keywords:

#: has the same semantics as C's . #-> has the same semantics as C's ->

For example:

vars point = initCoord(false);
4 -> point #: x; 10 -> point #: y;
    point =>
    ** struct _two_shorts
        x - 4
        y - 10

Valid return types for procedures

External procedures can now be given any return type that can be parsed by the typedef statement. Eg:

    Coord MousePosition();
        {}
    MousePosition() =>
    ** struct _two_shorts
        x - 39
        y - 51

define:c_type

The define-form "c_type" can be used to define arbitray "return types". Ie types that can be used to specify the coercion to be performed on the data returned by external C functions.

Eg. imagine the following C code has been compiled into a file 'example.o'

struct _two_shorts {short x,y;};
    struct _two_shorts *func()
    {
        static struct _two_shorts grum;
        grum.x = 3;
        grum.y = 4;
        return(&grum);
    }

The function, and its return data, can be accessed as follows:-

uses newexternal;
external declare test in c;
typedef struct _two_shorts {short x,y;} Coord;
        Coord *func()
            {}
endexternal;
    external load test;
        'example.o'
    endexternal;
    func()(0) =>
    ** struct _two_shorts
        x - 3
        y - 4

Alternatively, if you wanted the data to be returned in a two element pop11 vector then you could define an c_type as follows;

uses newexternal;
external declare test in c;
typedef struct _two_shorts {short x,y;} Coord;
endexternal;
    define:c_type twovec(eptr);
        lvars eptr;
        initCoord(eptr) -> eptr;
        {%
            eptr #: x,
            eptr #: y
         %}
    enddefine;
external declare test in c;
        twovec func()
            {}
endexternal;
    external load test;
        'example.o'
    endexternal;
func() => ** {3 4}

Varargs

If an external procedure contains a "vararg" list as one of its formal parameters then, within "external ... endexternal" syntax, it can be declared as being a procedure which takes just one argument which is typed as -vararg-.

Eg.

external declare PrintStuff in c
        printf(dummy);
            vararg dummy;
            {}
endexternal;

When the procedure is called, there must be an integer on the top of the stack to indicate the *total* number of arguments to be passed to the external procedure (in this case, including the format string as well as the printed data). A convenient way to do this is with the syntax word #| See REF * #|

Eg.

printf(#| string, item1, item2 |#);

NB1. Some C procedures require you to terminate a varargs list with one or more zeros. This mechanism is *not* a replacement of that, ie. the zeros must be included inside the #| .... |# construct.

Importing procedures

The global variable -external_import_procedure- is used to control how an external procedure is imported into the Poplog system during the "external load .... endexternal" phase. Its value, which is a procedure which coerces an external pointer into an external procedure, is usually one of -external_ptr_to_procedure- (the default) or -XptImportProcedure-.

Efficiency Notes

The syntax construct "exacc" can be used in conjunction with C structure types in order to produce inline access code.

Eg.

uses newexternal;
external declare test in c;
typedef struct _two_shorts {short x,y;};
endexternal;
vars foo = init_two_shorts(false);
5 -> exacc :_two_shorts foo.x;
    foo =>
    ** struct _two_shorts
        x - 5
        y - 0

Also the example given above for the use of define:c_type could be written more efficiently using -exacc-.

Eg.

    l_typespec twonums {
            one :short,
            two :short
        };
    define:c_type twovec(twonums);
        lvars twonums;
        {%
            exacc twonums.one;
            exacc twonums.two;
         %}
    enddefine;

Warning

    This version of "c_dec" provides minimal help with the coercion of
arguments, unlike the previous version. This enables the user to develop
more efficient interface functions, but at the cost of using data
structures that are more "terse".

See also

    HELP * EXTERNAL - details of the old style C interface
    LIB * NEWC_DEC  - all the gory details of the new interface
    REF * DEFSTRUCT - defining Poplog, and external, structure types
    REF * EXTERNAL  - a more down-to-earth approach to accessing
                      external data and code

Known Bugs

  1. Pointers are treated as arrays
  2. -external_import_procedure- should be local to each "declare" tag.
  3. Unions aren't integrated with -exacc- in the way that ordinary structures are.

--- C.all/help/newc_dec --- Copyright University of Sussex 1990. All rights reserved. ----------