For Development HEAD DRAFTSearch (procedure/syntax/module):

9.23 gauche.native-type - ネイティブタイプユーティリティ

Module: gauche.native-type

This module provides a type infrastructure to communicate with the “native”, or “close to the metal” layer. Examples include foreign function interface (FFI) and structured binary data access.

Basic native types are built-in (see ネイティブタイプ). This module adds constructors for derived or aggregate types (such as arrays and structs), endian-specified types, conversions between native type objects and compact signatures, and so on.

This module also provides native handles, via which you access native data from Scheme world (see Native handles).


9.23.1 Derived and aggregate native types

We have subclasses of <native-type>, which are classes of individual derived or aggregate native types. For example, “pointer to int” native type is an instance of <c-pointer> class. Note that <c-pointer> itself is not an instance of <native-type>.

C pointers

Class: <c-pointer>

A class of native types representing pointers. Can be created with make-c-pointer-type, or from a signature with native-type (e.g. (native-type 'int*), see Native type signature).

Note: At the moment, we don’t keep track of quoalifiers such as const or volatile, but their support may be added in future.

A native handle with a C pointer type can be “dereferenced” with native* (see Native handles).

Function: make-c-pointer-type pointee-type

{gauche.native-type} The argument must be an native type instance. Returns an native type represents a pointer to the pointee-type.

(make-c-pointer-type <int>)
  ⇒ #<c-pointer <int*>>

(make-c-pointer-type (make-c-pointer-type <int>))
  ⇒ #<c-pointer <int**>>
Function: c-pointer-type-pointee c-pointer-type

{gauche.native-type} Returns the pointee type of the given c-pointer type.

(c-pointer-type-pointee (make-c-pointer-type <int>))
  ⇒ #<native-type <int>>

C arrays

Class: <c-array>

{gauche.native-type} A class of native types representing C-style arrays. Can be created with make-c-array-type, or from a signature with native-type (e.g. (native-type '(.array int (10))), see Native type signature).

Arrays can have more than one dimensions. Elements with consectuve last indexes are laid adjacent on memory. The length of the first dimension can be left unspecified (we use * as a placeholder), just like C array such as int a[][10][3].

An element, or a subarray, of an array represented by a native handle with a C array type can be accessed with native-aref (see Native handles).

Function: make-c-array-type element-type element-type dimensions

{gauche.native-type} Creates a C array type, whose elements are of type element-type, which must be a native type.

The dimensions argument must be a list of non-negative exact integers, except that the first element may be a symbol *. They specify the lengths of each dimension of the array. Each index of dimensions starts with 0. * in the first dimension indicates the length is unspecified.

Function: c-array-type-dimensions c-array-type

{gauche.native-type} Returns a list of lengths of dimensions of c-array-type.

C functions

Class: <c-function>

{gauche.native-type} A class of native types representing C function types. Can be created with make-c-function-type, or from a signature with native-type (e.g. (native-type '(.function (int int) ::int)), see Native type signature).

Instances of <c-function> describe the signature of a C function: its return type, argument types, and whether it accepts variable arguments.

A native handle with a <c-function> type can be cast to and from a pointer-to-function type with cast-handle (see Native handles).

Function: make-c-function-type return-type argument-type-list

{gauche.native-type} Returns a native type representing a C function that returns return-type and takes arguments of the types listed in argument-type-list. Both return-type and each element of argument-type-list must be native type instances, with an exception below.

The last element of argument-type-list can be the symbol ..., and then the resulting type represents a variadic function (like C’s ... in int printf(const char*, …)). The ... marker is not a native type and is not included in the result of c-function-type-argument-types.

(make-c-function-type <int> `(,<int> ,<double>))
  ⇒ #<c-function  int double -> int>

(make-c-function-type <int> `(,<int> ...))
  ⇒ #<c-function  int ... -> int>

(c-function-type-argument-types (make-c-function-type <int> `(,<int> ...)))
  ⇒ (#<native-type <int>>)

(c-function-type-variadic? (make-c-function-type <int> `(,<int> ...)))
  ⇒ #t
Function: c-function-type-return-type c-function-type

{gauche.native-type} Returns the return type of c-function-type as a native type instance.

Function: c-function-type-argument-types c-function-type

{gauche.native-type} Returns the list of argument types of c-function-type. Each element is a native type instance. The variadic marker ... is not included; use c-function-type-variadic? to check for variadic functions.

Function: c-function-type-variadic? c-function-type

{gauche.native-type} Returns #t if c-function-type represents a variadic function (i.e., it was created with ... as the last element of the argument-type list), #f otherwise.

C structs and unions

Class: <c-struct>
Class: <c-union>

{gauche.native-type} A class of native types representing C struct and union types, respectively. Can be created with make-c-struct-type and make-c-union-type, or from a signature with native-type (e.g. (native-type '(.struct t (a::int b::char))), see Native type signature).

Function: make-c-struct-type tag fields
Function: make-c-union-type tag fields

{gauche.native-type} Returns a native type representing a C struct type or a C union type. tag can be a symbol representing the struct/union tag, or #f for untagged struct/union.

fields is a list of (symbol type), where symbol names the field’s name, and type for its type. type must be an instance of a native type.

When a struct/union type is constructed, its total size and each field’s offset are computed (in unions, all the offsets are 0).

Function: c-struct/union-type-tag c-struct/union-type

{gauche.native-type} Returns the tag (symbol) of c-struct/union-type. If the type is untagged, #f is returned.

Function: c-struct/union-type-field-names c-struct/union-type

{gauche.native-type} Returns a list of field names (symbols) of c-struct/union-type.

Function: c-struct/union-type-field-type c-struct/union-type field-name
Function: c-struct/union-type-field-offset c-struct/union-type field-name

{gauche.native-type} Returns the type and the byte offset of field-name (symbol) of c-struct/union-type.

9.23.1.1 Predicates

Function: c-pointer-type? obj
Function: c-array-type? obj
Function: c-function-type? obj
Function: c-struct-type? obj
Function: c-union-type? obj

{gauche.native-type} Returns #t if obj is an instance of <c-pointer>, <c-array>, c-function>, c-struct, or <c-union>, and #f otherwise, respectively.

Function: c-pointer-like-type? obj

{gauche.native-type} Returns #t if obj is either a C pointer type, a C array type, or a C function type. Handles of those types can be “casted” to a pointer type, following the C semantics. See cast-handle for the details (see Native handles).

Function: c-aggregate-type? obj

{gauche.native-type} Returns #t if obj is either a C array type, a C struct type, or a C union type.

Function: c-struct/union-type? obj

{gauche.native-type} Returns #t if obj is either a C struct type, or a C union type.


9.23.2 Representation-specific types

Primitive native types such as <int16> read/write memory with the system’s native endianness. For FFI it is fine. However, if you read/write binary data for exchanging between systems, you need to explicitly specify the endianness.

We have native types for that purpose.

Native type: <int16-be>
Native type: <int16-le>
Native type: <int32-be>
Native type: <int32-le>
Native type: <int64-be>
Native type: <int64-le>
Native type: <uint16-be>
Native type: <uint16-le>
Native type: <uint32-be>
Native type: <uint32-le>
Native type: <uint64-be>
Native type: <uint64-le>
Native type: <float-be>
Native type: <float-le>
Native type: <double-be>
Native type: <double-le>

{gauche.native-type} These native types accesses binary data as the specified numeric type, and the specified endianncess (-be for big-endian, -le for little endian).

The endianness is only considered when reading from or writing to memory through native handles (with native* etc, see Native handles). In other words, these types only makes sense when they are used within derived or aggregate types.


9.23.3 Native type signature

A native type signature is a compact S-expression description of a native type. It lets you specify pointer, array, struct, union, and function types without explicitly calling the corresponding constructors such as make-c-pointer-type.

The procedure native-type can parse a signature and returns the native type instance it denotes; native-type->signature performs the inverse conversion. Signatures are also recognized in some other places where a native type is expected—for example, in struct/union field type declarations within .struct and .union signature forms (so compound types can be written without first naming each piece).

The signature format has been used in Gauche-C interface for long time (stubs and CiSE), though hasn’t been documented well. Because of its origin to transfer C type declarations to S-expression, it may feel C-centric.

Primitive types

A bare symbol whose name matches a built-in C type name denotes the corresponding primitive native type. Recognized names follow the C spelling, e.g. int, u_int, short, u_short, long, u_long, char, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, size_t, ssize_t, ptrdiff_t, off_t, intptr_t, uintptr_t, float, double, and void. See ネイティブタイプ for the corresponding type instances and what Scheme values they accept.

C’s char denotes the native type <c-char>, for <char> is used for Gauche’s character type. <c-char> is mapped to a Scheme character with unicode range U+0000 to U+00FF. If the C routine uses char as a byte-sized numeric value, use int8_t or uint8_t (depending on signedness) instead.

The pseudo-name c-string denotes the <c-string> type (NUL-terminated C string). This special name is provided because C type const char * may simply mean a pointer to byte-sized numeric data, thus ambiguous.

The endian-specified types listed in Representation-specific types are also accepted, by their underscore-separated names: int16_le, int16_be, uint16_le, uint16_be, and likewise for the 32-bit and 64-bit integer types and for float and double.

Pointer types

A symbol whose name ends with one or more asterisks denotes a pointer type; each trailing asterisk wraps the rest of the type in one additional layer of <c-pointer>. Thus int* is equivalent to (make-c-pointer-type <int>), char** is a pointer to a pointer to <c-char>, and so on.

C-style multi-token forms are also accepted as a list of symbols. The list is normalized by stripping any const tokens and concatenating the rest into a single symbol. Therefore (char *), (char const*), (const char*), (int * *), and (int** *) are all valid. The form (const type) is also accepted, with const currently ignored.

(We do not yet track const or volatile qualifiers on the resulting native type; they are silently dropped. Support may be added in the future.)

Array types

(.array element-type (dim ...))

Denotes a C-style array. element-type is itself a native type signature. Each dim is a non-negative exact integer, except that the leftmost dim may be the symbol * to indicate an unspecified leading dimension (as in C’s int a[][3][4]). Multiple dimensions describe a multi-dimensional array, with elements that share leading indices laid out adjacently in memory (the last index varies fastest).

Struct and union types

(.struct tag (field-spec ...))
(.struct (field-spec ...))
(.union  tag (field-spec ...))
(.union  (field-spec ...))

Denote a C struct or union type. tag, when given, is a symbol that names the struct/union tag; the second form of each pair (with no tag) denotes an anonymous struct/union.

Each field-spec has the form name::type, using the typed-variable notation common across Gauche. name is a symbol naming the field, and type is itself a native type signature, so fields may be of compound type as well. For the convenience, the delimiter :: may be concatenated to name and/or type– for example, a::int and a :: int are equivalent.

Function types

(.function (arg-type ...) return-type)

Denotes a C function type. Each arg-type and return-type is itself a native type signature. If the last arg-type is the literal symbol ..., the resulting type is variadic (corresponding to C’s trailing ...); the ... marker is not itself a type, and is not included in the argument-type list returned by c-function-type-argument-types. Use c-function-type-variadic? to test for variadicity.

Pass-through

If the argument is already an instance of <native-type>, native-type returns it unchanged. This makes it convenient for higher-level routines to accept either a signature or a previously constructed type instance.

Notably, you can emulate C’s typedef by assigning native type to a variable, and inserting it in another type signature:

(define foo (.struct foo (a::int b::int)))

(native-type `(.array ,foo (10))) ⇒ #<c-array struct foo(10)>

Examples

(native-type 'int)
  ⇒ #<native-type <int>>

(native-type 'int*)
  ⇒ #<c-pointer <int*>>

(native-type 'char**)
  ⇒ #<c-pointer <c-char**>>

(native-type '(char const *))
  ⇒ #<c-pointer <c-char*>>

(native-type 'c-string)
  ⇒ #<native-type <c-string>>

(native-type '(.array int (3 4)))
  ⇒ #<c-array <int>(3 4)>

(native-type '(.array int (* 3)))
  ⇒ #<c-array <int>(* 3)>

(native-type '(.struct point (x::double y::double)))
  ⇒ #<c-struct struct point>

(native-type '(.union v (i::int f::float)))
  ⇒ #<c-union union v>

(native-type '(.function (int int) double))
  ⇒ #<c-function <int> <int> -> <double>>

(native-type '(.function (char* ...) int))
  ⇒ #<c-function <c-char*> ... -> <int>>
Function: native-type signature-or-type

{gauche.native-type} The argument must be an S-expression type signature or a native type instance. If it is a type signature, a native type instance denoted by the type signature is returned. If the argument is already a native type instance, it is returned as is.

See above for the specification of a native type signature.

Function: native-type->signature native-type

{gauche.native-type} Returns a native type signature that denotes the given native type.


9.23.4 Native handles

A native handle is a handle through which you can access native data. Native handles may be returned from a foreign function call, or you can create one from a binary buffer. C pointers, arrays, structs, and unions to be passed to and from foreign functions are reprensed by native handles.

Native handle class and predicates

Class: <native-handle>

{gauche.native-type} The class of native handle. A native handle is either a typed reference of, or a typed pointer to a native object. Its type can be retrieved with native-handle-type. The pointed or referenced object is retrieved with type-specific accessors below.

A native handle can be of c-pointer, c-array, c-function, c-struct or c-union types. If it’s other than c-pointer type, native handle holds a reference. The distinction of pointers and references is vague in the Scheme world, since all heap-allocated Scheme objects are treated indirectly. But there’s a distinction in the C/C++ world; the following example session shows it.

(define inner (native-type '(.struct (a::int b::int))))
(define outer (native-type `(.struct (ref::,inner ptr::(,inner *)))))

(define handle (uvector->native-handle (make-u8vector (~ outer'size)) outer))

(set! (native. (native. handle 'ref) 'a) 1)  ; handle.ref.a = 1
(set! (native. (native. handle 'ref) 'b) 2)  ; handle.ref.b = 2

(set! (native. handle 'ptr) (native& handle 'ref)) ; handle.ptr = &handle.ref

(define inner-ref (native. handle 'ref)) ; inner& inner_ref = handle.ref
inner-ref ⇒ #<native-handle struct anonymous@0x77a1b10f51f0>

(define inner-ptr (native. handle 'ptr)) ; inner* inner_ptr = handle.ptr
inner-ptr ⇒ #<native-handle <struct anonymous*>@0x77a1b10f51f0>

(native. inner-ref 'a)  ⇒ 1           ; inner_ref.a
(native-> inner-ptr 'a)  ⇒ 1          ; inner_ptr->a

(native. (native* inner-ptr) 'b) ⇒ 2 ; (*inner_ptr).b
Function: native-handle-type handle

{gauche.native-type} Returns the native type of the handle.

Function: c-pointer-handle? obj

{gauche.native-type} Returns #t if obj is a native handle of c-pointer type, #f otherwise.

Function: c-function-handle? obj

{gauche.native-type} Returns #t if obj is a native handle of c-function type, #f otherwise.

Function: c-array-handle? obj

{gauche.native-type} Returns #t if obj is a native handle of c-array type, #f otherwise.

Function: c-struct-handle? obj
Function: c-union-handle? obj

{gauche.native-type} Returns #t if obj is a native handle of c-struct and c-union type, respectively, #f otherwise.

Function: c-aggregate-handle? obj

{gauche.native-type} Returns #t if obj is a native handle of c-array, c-struct, or c-union type, #f otherwise.

Function: c-pointer-like-handle? obj

{gauche.native-type} Returns #t if obj is a native handle of c-array, c-pointer, or c-function type.

Accessing through native handles

Function: native* handle

{gauche.native-type} Pointer dereference. handle must be a c-pointer handle. Returns the pointed value boxed as a Scheme object. if the pointed value is an object of c-pointer or c-aggregate types, a native handle representing that object is returned.

Function: (setter native*) handle val

{gauche.native-type} You can use a generic setter to modify the value in the pointed location. val must be a value acceptable as the pointee type of the pointer.

Function: native-aref handle index

{gauche.native-type} Array reference. handle must be a c-array handle or c-pointer handle (Allowing c-pointer here reflects the C semantics).

index must be a nonnegative integer or a list of nonnagative integers. If the element type is a primitive type, the value is returned as a Scheme value. If the element type is a pointer type or an aggregate type, the value is wrapped as a native handle.

C’s multidimensional array is effectively nested one-dimensional arrays. If you give indexes less than the rank of the array, a handle to the subarray is returned.

If the handle points to an unbounded c-array or a c-pointer, no boundary check is done with index. It is caller’s responsibility to ensure index falls in a valid range.

Function: (setter native-aref) handle index val

{gauche.native-type} handle must be a c-array or c-pointer handle, and index must be a nonnegative integer or a list of nonnagative integers.

Sets val to the array at the position specified by index.

If the handle points to an unbounded c-array or a c-pointer, no boundary check is done with index. It is caller’s responsibility to ensure index falls in a valid range.

Function: native. handle slot

{gauche.native-type} Struct/union field reference. handle must be a native handle of c-struct or c-union type, and slot must be a symbol naming its slot. An error is thrown if the struct/union doesn’t have the named slot.

Function: (setter native.) handle slot val

{gauche.native-type} Sets val to the field slot of c-struct or c-union handle.

Function: native-> handle slot

{gauche.native-type} Indirect struct/union field reference. handle must be a native handle of c-pointer to a c-struct or c-union. (native-> handle slot) is the same as (native. (native* handle) slot), just like s->a and (*s).a are the same in C.

Function: (setter native->) handle slot :optional type

{gauche.native-type} Sets val to the field slot of c-struct or c-union pointed by a c-pointer handle handle.

Function: native& handle selector

{gauche.native-type} This takes the pointer to an element inside an aggregate data referenced by handle. The handle must be a native handle fo type c-array, c-struct, or c-union. An error is signaled if other type of native handle is given.

If handle is a c-struct or c-union, selector must be a symbol naming its field. A c-poitner handle points to the field is returned.

If handle is a c-array, selector must be a fixnum, or a list of fixnums, as the index to the array. If the array is 1-dimensional, a fixnum or a list of signle fixnum is accepted. A c-pointer handle points to the element is returned.

(define ts (native-type '(.struct (a::int))))
(define hs (uvector->native-handle (make-u8vector (~ ts'size)) ts))

(native& hs 'a) ⇒ #<native-handle <int*>@0x726ffcc1a060>

(define ta (native-type '(.array int (10))))
(define ha (uvector->native-handle (make-u8vector (~ ta'size)) ta))

(native& ha 3) ⇒ #<native-handle <int*>@0x726ffcccd42c>

Handle operations

Function: cast-handle type handle :optional offset

{gauche.native-type} Returns a new native handle that points to the same memory region of handle, but with type. Optionally, you can specify a byte offset with offset from the handle.

You can cast a pointer to another pointer, an aggregate (struct, union, array) to another aggregate, an array to a pointer or a pointer to an array.

If handle knows the valid memory range it references, it checks if type falls within the region. However, if the handle is obtained from an external C function call, the valid memory region isn’t often explicit, and it’s up to you to treat it safely.

Function: c-pointer-compare handle-a handle-b

{gauche.native-type} Both handle-a and handle-b must be pointer-like handles. It compares the value of those pointer values, and returns either -1 (if handle-a is smaller), 0 (if both are the same), or 1 (if handle-a is greater).

Function: c-pointer=? handle-a handle-b
Function: c-pointer<? handle-a handle-b
Function: c-pointer<=? handle-a handle-b
Function: c-pointer>? handle-a handle-b
Function: c-pointer>=? handle-a handle-b

{gauche.native-type} Both handle-a and handle-b must be pointer-like handles. It compares the value of those pointer values,

Function: c-memwise-compare handle-a handle-b

{gauche.native-type} Both handle-a and handle-b must be references to aggregate type objects. Returns either -1, 0, or 1, depending on whether handle-a is smaller, the same, or handle-a is greater. The actual types of two handles may differ.

First, the sizes are compared. If they differ, that’s the result. If the sizes are the same, contents are compared bytewise. This is the same as how bytevector-comparator compares two bytevectors (see 用意されている比較器).

Function: copy-handle-memory! dst-handle src-handle :optional size offset

{gauche.native-type} Copy the memory content of size bytes pointed or reference by src-handle to the memory pointed or reference by dst-handle.

If size argument is omitted or #f, bytes of the size determined by the type of src-handle are copied.

Optional offset allows to copy from non-zero offset. If integer size is given, size bytes from src-handle + offset are copied. If size is #f, from src-handle + offset to the end of the type size are copied.

Creating a native handle

Function: uvector->native-handle uv type :optional offset

{gauche.native-type} This creates a native handle of native type type, using a uniform vector uv as the backing storage. This is useful to prepare binary data in Scheme world. type must be a pointer or an aggregate native type.

uv can also be #f, in which case a u8vector of size to contain object of type is allocated and used.

If offset is given, it specifies a byte offset from the beginning of uv for the resulting handle to point to. Using offset, you can extract a native handle in the middle of larger binary data. If it is omitted, 0 is assumed.

The size of uv in bytes minus offset must be greater than or equal to the size of type, or an error is thrown.

The returned native handle can be passed to a foreign function. Note that, however, if the foreign function retains the pointer, it may not be visible from Gauche’s GC. You have to make sure to retain reference to uv or the resulting native handle in the Scheme world as far as the pointer is retained in the foreign world.

You can use any type of uniform vector as uv, but other than u8vector and s8vector, the machine’s native endianness is exposed as the binary data.

(define ip (uvector->native-handle '#u8(#xff #xff #xff #xff)
                                   (native-type 'int32_t*)))

ip ⇒ #<native-handle <int32*>@0x7d6fbf442ff0>

(native* ip) ⇒ -1
Function: null-pointer-handle :optional type

{gauche.native-type} Returns a c-pointer handle representing NULL pointer. The optional argument type must be a c-pointer type and specifies the type of the resulting handle. When omitted, <void*> native type is assumed. (For FFI calls, you can pass <void*> pointer to wherever a pointer is expected.)

Function: null-pointer-handle? obj

{gauche.native-type} Returns #t if obj is a native handle representing NULL pointer (of any c-pointer type), #f otherwise.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT