Low level support facilities

The high level functions for calling C routines or for accessing global variables are all built upon a relatively small number of built-in primitives which perform specific low-level tasks. You should seldom have any need to deal with these primitives directly, but they are nonetheless available should you need to make use of them. To use these types and operations, you should "use" the module "system" from the "Dylan" library.

Predefined types

<statically-typed-pointer> [class] 

Unless otherwise specified, C pointers are implicitly "equated" to newly created subclasses of <statically-typed-pointer>. This class is contains a single implicit slot which contains the raw C pointer. Because of implementation limitations in Mindy, you may not add any extra slots to subclasses of <statically-typed-pointer>, nor can such a subclass inherit slots from other classes. You may, however, create classes which are subclasses of both <statically-typed-pointer> and other (presumably abstract) classes which have no slots.

The "make" method for takes three keywords. The "pointer:" keyword tells it to initialize the new variable with the given value, which must be a <statically-typed-pointer> or an <integer>. If the no pointer value is specified, space will be allocated based upon the content-size of the specific type and upon the "extra-bytes:" and "element-count:" keywords. These keywords, which default to "0" and "1" respectively, tell how many objects are going to be stored in the memory and how many bytes of extra memory (beyond that specified by "content-size") should be allocated for each element.

<c-vector> [class]

<C-vector> is a subclass of <statically-typed-pointer> which inherits operations from <vector>. Because systems often depend upon C's lack of bounds checking, the default size for <c-vector>s is "#f". However, subclasses of <c-vector> may provide a concrete size if desired. Types corresponding to declarations such as "char [256]" are automatically declared as subclasses of <c-vector>, but pointer declarations such as "char *" are not.

<c-string> [class] 

<C-string> is a subclass of <statically-typed-pointer> which also inherits operations from <string>. It is implemented as a C pointer to a null-terminated vector of characters, and provides a method on forward-iteration-protocol which understands this implementation. This class may, therefore, be used for manipulating C's native format for "string"s (i.e. "char *"). Note that the "null" string is considered to be a valid empty string. This is somewhat contrary to the semantics of many C operations, but provides a safer model for Dylan code.

The "make" method for <c-string>s accepts the "size:" and "fill:" keywords.

There are a few surprising properties of <c-strings> which may users should be aware of, both of which result from the "null-terminated" implementation. Firstly, the "size" of the string is computed by counting from the beginning of the string, and is therefore not nearly as efficient as you might expect. Secondly, you should expect odd results if you try to store "as(<character>, 0)" into such a string. Finally, the "element" and "element-setter" methods must scan the string in order to do bounds checking, and may therefore be fairly slow. If you wish to (unsafely) bypass this checking, you must use "pointer-value" instead.

<c-function> [class] 

<c-function>s, like <statically-typed-pointer>s, encapsulate a raw "C" pointer. However, <c-function>s also encode information about the calling conventions of the function which is (presumably) located at the given address. They may, therefore, be directly invoked just like any other function.

<foreign-file> [class] 

The <foreign-file> class is used to store information about the contents of a particular object file. It is created by "load-object-file", and may be passed as an option to "find-c-function" and "find-c-pointer". (All of these functions are described below.)

Locating native C objects

There are several functions provided which search for C functions or variables and return Dylan objects which refer to them. Note that Mindy does not have sufficient information to determine whether any given C object is a function, and therefore it depends upon the user (or, more often, Melange) to provide it with correct information.

load-object-file(files :: <list>, #key symbols) [function]

This function (which is presently only works on HPUX machines) attempts to dynamically load a given object file (i.e. ".o" or ".a") into the current Mindy process and load itUs symbol table to allow its contents to be located by "find-c-pointer" or "find-c-function". If it successfully loads the file, it will return a <foreign-file> encapsulating the symbol table information. Otherwise, it will return #f.

If you are not running on an HPUX machine, you will have to statically link object files into Mindy, as described in Chapter II.

find-c-pointer(name :: string, #key file :: <foreign-file) [function] 

This function searches through the symbol table for the object file corresponding to the specified file (or for Mindy itself) and attempts to locate a symbol with the given name. If it finds such a symbol, it converts the corresponding address to a <statically-typed-pointer> and returns it. Otherwise, it returns #f.

find-c-function (name :: <string>, #key file) [function] 

constrain-c-function (fun :: <c-function>, [function] 

specializer :: <list>, rest? :: <boolean>, 

results :: <list>) 

The function "find-c-function" is like "find-c-pointer", except that the result is a <c-function> (or #f). The resulting function is specialized to "fun(#rest args) :: <object>". However, it may be constrained to a different set of specializers via "constrain-c-function". This function accepts lists of types for the arguments and for the return values, and a boolean value which states whether optional arguments are accepted. The result declarations are particularly important, since they are used to coerce the raw C result value into an appropriate low level Dylan type. The possible types are <boolean>, <integer>, or any subclass of <statically-typed-pointer>. Note that although a list of result types is accepted, only the first can be meaningful since C does not support multiple return values.

Note: The functions in this section are likely to change drastically in the near future.

Pointer manipulation operations

Each <statically-typed-pointer> encapsulates a pointer to some area of memory (i.e. a raw machine address). In itself, this does little good, except as an arbitrary token. However, Mindy provides a number of primitive operations which manipulate the contents of these addresses, or do basic comparisons and arithmetic upon the addresses themselves.

signed-byte-at (ptr :: <statically-typed-pointer>, #key offset) [function] 

unsigned-byte-at (ptr :: <statically-typed-pointer>, #key offset)

signed-short-at (ptr :: <statically-typed-pointer>, #key offset) [function] 

unsigned-short-at( ptr :: <statically-typed-pointer>, #key offset)

signed-long-at (ptr :: <statically-typed-pointer>, #key offset) [function] 

unsigned-long-at (ptr :: <statically-typed-pointer>, #key offset)

pointer-at (ptr :: <statically-typed-pointer>, [function] 

#key offset, class) 

These operations return an object which represents the value stored at the address corresponding to "ptr". The first six operations all return <integer>s -- the different versions are required because the same number may be represented in a variety of formats (differing in length and interpretation of the high-order bit) and because Mindy has no way of determining which might be used in a given situation. The final operation, "pointer-at", returns a new <statically-typed-pointer> encapsulating the address referenced by the origninal pointer. You may use the "class:" keyword to specify that the new object should be an instance of some particular subclass of <statically-typed-pointer>. (Thus, for example "pointer-at(foo, class: <bar>)" would be roughly equivalent to "as(<bar>, pointer-at(foo))".)

The offset parameter (if provided) is added to the integer corresponding to the machine address before the pointer is dereferenced. This is useful, for example, in loading an object from within a C "struct".

Setter functions are provided corresponding to each of the above functions. You can therefore, say

signed-short-at(ptr) := 32767;
pointer-at(ptr1) := ptr2;

as(cls == <integer>, ptr :: <statically-typed-pointer>) [G.F. Method] 

as(cls == <statically-typed-pointer>, ptr :: <statically-typed-pointer>) 

[G.F. Method] 

as(cls == <statically-typed-pointer>, int:: <integer>) [G.F. Method] 

Method upon "as" are provided for converting from <integer> to any statically typed pointer class and from any statically typed pointer class to <integer> or to another statically typed pointer class.

\+ (ptr :: <statically-typed-pointer>, int :: <integer>) [G.F. Method] 

\- (ptr1 :: <statically-typed-pointer, ptr2 :: <statically-typed-pointer>) 

[G.F. Method] 

\= (ptr1 :: <statically-typed-pointer, ptr2 :: <statically-typed-pointer>) 

[G.F. Method] 

These functions do arithmetic upon the integers corresponding to the given pointers. The following code fragment

let new-ptr = ptr1 + 3;
let difference = ptr2 + ptr3;
let same? = (ptr2 = ptr3)

is equivalent to

let new-ptr = as(ptr1.object-class, as(<integer>, ptr1) + 3);
let difference = as(<integer>, ptr2) - as(<integer>, ptr3);
let same = (as(<integer>, ptr2) = as(<integer>, ptr3));