Extern and Opaque in ZPL

This page is meant to give a summary of how to use the extern/opaque keyword in ZPL programs. If you find inconsistencies between this document and your use of ZPL, or bugs in the extern/opaque keywords, please let us know at zpl-bugs@cs.washington.edu.

Extern Overview

The extern keyword is used to allow you to refer to procedures, constants, types, and variables external to your ZPL program (for example, in an external C library or object file). It can be used in combination with the opaque keyword to allow the interaction between ZPL programs and external routines.

Using extern

extern can essentially be tacked to the front of declarations for prototypes, types, constants, and variables, to indicate that the ZPL compiler should not generate the definitions, but that they will be provided by an extern module. For example:

     extern prototype random():longint;

     extern constant MPI_VERSION:integer;

     extern type timeval = record
                             tv_sec:longint;
                             tv_usec:longint;
                           end;

     extern var errno:integer;

Each of these external declarations lets the ZPL compiler know the name and type of a construct that the user promises to supply outside of ZPL. This gives ZPL the ability to refer to the name and type of the external construct without dealing with the declaration and definition of the construct.

To complete the use of an external type, you must supply two things: (1) a C header file that gives a C declaration for the external object. This will typically be the header file that defines the interface for the module that you are linking to; and (2) a library, object file, or C source file that defines the external construct for linking in (if it's part of the standard C or math libraries, there's no need to add anything new, as those are automatically linked in by the ZPL compiler). Both of these can be specified on the zc command line. For example:

     zc my_header.h my_implementation.o my_zpl_src.z

This will cause my_header.h to be #defined in the C file created by the ZPL compiler for my_zpl_src.z, and for my_implementation.o to be linked to the resulting object code when forming the executable.

Opaque types and partially-specified types

In some cases, the type of an external concept may be unspecifiable in ZPL, or unimportant to the ZPL program. In these cases, the type opaque can be used to indicate to that ZPL compiler that you can't express the type, but that it doesn't need to be concerned with it. For example, if I have a char** variable "str_handle" that needs to be passed around from one external procedure to the next, I'd be hard-pressed to describe that in ZPL, but I also wouldn't be able to manipulate it much in ZPL since ZPL doesn't have pointer-dereferencing operators. Thus, I could declare it opaquely as follows:

     extern var str_handle:opaque;

This lets the ZPL compiler know that I will provide it with a variable named str_handle, but that it doesn't need to worry about its type. I can then pass this into external procedures that take a char** by prototyping their arguments as being opaque and passing the str_handle into it:

     extern prototype munge_str(a_str_handle:opaque);

     ...

     munge_str(str_handle);

Similarly, external types can be declared as opaque and even used to declare non-external variables. For example, imagine that I want to call into C's gettimeofday() routine. I will want to check the timeval structure, but don't care so much about the timezone structure. I might do this as follows:

     extern type timeval = record
                             tv_sec:longint;
                             tv_usec:longint;
			   end;
			
     extern type timezone = opaque;

     extern prototype gettimeofday(var tv:timeval; var tz:timezone);

     var
        my_tv:timeval;
	my_tz:timezone;

     gettimeofday(my_tv,my_tz);

     writeln("Time is %d seconds":my_tv.tv_sec, ", %d microseconds":my_tv.tv_usec);

Note that I can tell ZPL about the specifics of the types that I care about (timeval, in this example), but leave out the details of types that don't concern me (like timezone). These declarations tell the ZPL compiler the names of both types and the description of the one that it needs to deal with, and allows me to declare ZPL variables of the specified types in either case, since the types will be supplied by my C header.

This abstracting of external types also allows for types to be partially specified. For example, in the above example, if I'm only concerned about the seconds field, but not the microseconds field, I could simply omit it from my declaration:

     extern type timeval = record
                             tv_sec:longint;
			     -- some other field(s) as well that I don't care about
                           end;

     var my_tv:timeval;

     writeln(my_tv.tv_sec);   -- this is OK
     writeln(my_tv.tv_usec);  -- this no longer makes sense

Why is this legal? Simply because ZPL isn't declaring the type -- it's external. So the only thing the ZPL compiler needs to know about are the names and identifiers that you're interested in referring to. The others will still be in the structure (since it is being declared by the C header file, not the ZPL compiler), but won't be known to the ZPL compiler.

Further Questions?

If you have further questions about the extern or opaque keywords in ZPL, please don't hesitate to contact us at zpl-info@cs.washington.edu.