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 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());
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 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.
#pragma pack
.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 } ]
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
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.
next
fieldIt 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 }]);
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.
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 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.
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;