Declaring types

Draft
This page is not complete.

The ctypes object offers a number of constructor methods that let you declare types. Every type is represented by a CType object, which, in turn, provides a constructor method you can call to define values of those types. Each type you declare using js-ctypes corresponds to a compatible C declaration.

Types are declared in terms of other, already defined types. At the core of all this is a set of predefined types provided by js-ctypes. These are all primitive types, upon which all other types are eventually based.

Primitive types

Primitive types are those types that represent a single value in memory, as opposed to arrays, structures, or functions. You can define values of these types without declaring new types. For example, to define a new 32-bit integer variable with the value 5:

var i = ctypes.int32_t(5);

You can then pass a pointer to this value to a C function that requires a pointer to a 32-bit integer, like this:

some_c_function(i.address());

Declaring new primitive types

There are times when you want to create new types that are simply new names for existing primitive types. For example, on Windows, you may wish to be able to use the Windows-standard DWORD type name for unsigned 32-bit integers. To declare this type, you can simply do:

const DWORD = ctypes.uint32_t;

After doing this, DWORD is a CType that can then be used to represent 32-bit unsigned integers.

Structures

Structures are declared using the ctypes.StructType() constructor. This method accepts as input the name of the structure and an array of field descriptors, each describing one field in the structure. You can, optionally, leave out the field descriptor array; this creates an opaque structure whose contents are not defined.

Note: At present, there isn't a way to specify that an array's C equivalent was declared as a packed structure; that is, using #pragma pack.

Field descriptors

Each field descriptor is comprised of a field name and its data type, represented as a string and a CType object, respectively. The format, then, looks like this:

[
  { field1: type1 },
  { field2: type2 },
  ...
  { fieldN: typeN }
]

Example

For example, to support the C tm structure using js-ctypes, you would use the following code:

const struct_tm = new ctypes.StructType("tm",
                        [ { "tm_sec": ctypes.int },
                          { "tm_min": ctypes.int },
                          { "tm_hour": ctypes.int },
                          { "tm_mday": ctypes.int },
                          { "tm_mon": ctypes.int },
                          { "tm_year": ctypes.int },
                          { "tm_wday": ctypes.int },
                          { "tm_yday": ctypes.int },
                          { "tm_isdst": ctypes.int } ]);

To find other types see here: Predefined Types - Primitive Types. You can then declare and use a function that uses this structure, like this:

// Declare the libc asctime() function, which returns a char * and accepts a pointer to a tm structure.

const asctime = lib.declare("asctime", ctypes.default_abi, ctypes.char.ptr, struct_tm.ptr);

var theTime = new struct_tm;
theTime.tm_hour = 3;
theTime.tm_min = 15;
...

var timeStr = asctime(theTime.address());    // Pass a pointer to the tm struct

var jsString = timeStr.readString();         // Convert the C string to JavaScript

The last line converts the C string returned by the libc asctime() function into a JavaScript string by calling the CData readString() method.

For another example see here: GetCursorPos

Opaque structures

An opaque structure is one whose content fields are not known, or are not intended to be accessed directly. They can also be used to handle forward declarations, by declaring a structure as opaque if it needs to include a structure that has yet to be declared. You can then define the fields in the opaque structure later by calling the CType object's define() method.

For example:

var someStructure = ctypes.StructType("someStructure");
var anotherStruct = ctypes.StructType("anotherStruct", [ {field1: opaque.ptr} ]);
someStructure.define([ { ptrToAnotherStruct: anotherStruct.ptr } ]);

As you see here, the two structure types here contain pointers to each other; by making one of them opaque at first, you can declare the fields on the other one, then define the fields on the first one afterward. This works around the lack of true forward references in JavaScript.

Structures with next field

It is common to see structures with a "next" field that is a pointer to itself. In order to accomplish this you can either set the type of next to ctypes.voidptr_t, or to be more accurate, use the define technique:

var struct = ctypes.StructType('struct');
struct.define([{ next: struct.ptr }]);

Arrays

To declare a new array type, you use the ctypes.ArrayType() method.When declaring a new array type, you provide a CType indicating the type of each element in the array as well as an optional array length. You may declare your array either with a specific number of elements, or with no predetermined size. This is permitted since C allows it, for the widest possible compatibility.

Declaring an array type with unspecified length

To declare a new array type without specifying a length, you simply pass in the CType specifying the element type when calling ctypes.ArrayType(). For example, to create a type for an array of C standard I/O FILE pointers (perhaps for tracking a number of active files on disk):

const FILE = new ctypes.StructType("FILE").ptr;      // Create FILE as a FILE * type
const FileArray = new ctypes.ArrayType(FILE);        // Create a FileArray type

In this example, FILE is an opaque pointer we can use to refer to C FILE records, as defined in stdio.h. FileArray is a new type representing an array of unspecified length, in which each entry is a pointer to a FILE record.

Declaring an array type with a specific length

Declaring an array type that specifies the array length is as simple as adding a length when calling ctypes.ArrayType(), like this:

const FileArray = new ctypes.ArrayType(FILE, 20);

This declares FileArray as an array type that can hold 20 elements.

Pointers

Declaring a pointer type as a pointer to a specific type is done by passing as a parameter to the ctypes.PointerType() method a CType object indicating the type to which the pointer should refer:

const IntPtr = new ctypes.PointerType(ctypes.int);

In this example, IntPtr is equivalent to this declaration in C:

typedef int *intPtr;

You can similarly declare a pointer type as a pointer to any user-defined type, including structures:

const UserRecord = new ctypes.StructType("UserRecord",
                        [{"name": ctypes.char.ptr},
                        {"id": ctypes.int32}]);
const UserRecordPtr = new ctypes.PointerType(UserRecord);

In this example, a new UserRecord type is defined, along with a new pointer type that can be used to reference it. The equivalent C code looks like this:

typedef struct UserRecord {
  char *name;
  int id;    // Assuming int is 32-bit here
} UserRecord;

typedef UserRecordPtr *UserRecord;