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).
| • Derived and aggregate native types: | ||
| • Representation-specific types: | ||
| • Native type signature: | ||
| • Native handles: |
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>.
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).
{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**>>
{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>>
{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).
{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.
{gauche.native-type}
Returns a list of lengths of dimensions of c-array-type.
{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).
{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
{gauche.native-type}
Returns the return type of c-function-type as a native type instance.
{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.
{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.
{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).
{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).
{gauche.native-type}
Returns the tag (symbol) of c-struct/union-type.
If the type is untagged, #f is returned.
{gauche.native-type}
Returns a list of field names (symbols) of c-struct/union-type.
{gauche.native-type}
Returns the type and the byte offset of field-name (symbol)
of c-struct/union-type.
{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.
{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).
{gauche.native-type}
Returns #t if obj is either a C array type,
a C struct type, or a C union type.
{gauche.native-type}
Returns #t if obj is either
a C struct type, or a C union type.
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.
{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.
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.
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.
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 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 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 (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.
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)>
(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>>
{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.
{gauche.native-type}
Returns a native type signature that denotes the given native type.
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.
{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
{gauche.native-type}
Returns the native type of the handle.
{gauche.native-type}
Returns #t if obj is a native handle of c-pointer type,
#f otherwise.
{gauche.native-type}
Returns #t if obj is a native handle of c-function type,
#f otherwise.
{gauche.native-type}
Returns #t if obj is a native handle of c-array type,
#f otherwise.
{gauche.native-type}
Returns #t if obj is a native handle of c-struct
and c-union type, respectively,
#f otherwise.
{gauche.native-type}
Returns #t if obj is a native handle of c-array,
c-struct, or c-union type,
#f otherwise.
{gauche.native-type}
Returns #t if obj is a native handle of c-array,
c-pointer, or c-function type.
{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.
{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.
{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.
{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.
{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.
{gauche.native-type}
Sets val to the field slot of c-struct or c-union handle.
{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.
{gauche.native-type}
Sets val to the field slot of c-struct or c-union
pointed by a c-pointer handle handle.
{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>
{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.
{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).
{gauche.native-type}
Both handle-a and handle-b must be pointer-like
handles. It compares the value of those pointer values,
{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 用意されている比較器).
{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.
{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
{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.)
{gauche.native-type}
Returns #t if obj is a native handle representing
NULL pointer (of any c-pointer type), #f otherwise.