Raku 原生类型
Raku 提供了一组原生类型,在内存中具有固定且已知的表示。此页面显示了存在哪些原生类型以及如何使用它们。有关它们的更多信息,请查看有关原生数字 的页面。
Types with native representation
Raku 中的一些简单类型具有原生表示,表示它们将使用编译器,操作系统和原生提供的 C 语言表示。这些是可用的四种原生类型:
int | Equivalent to Int (with limited range) |
uint | Equivalent to Int (with limited range) with the unsigned trait |
num | Equivalent to Num |
str | Equivalent to Str |
但是,这些类型不一定具有 NativeCall 接口所需的大小(例如,Raku 的 int
可以是 8 个字节,但 C 的 int
只有 4 个字节); 必须使用以下类型而不是上面列出的 int
或 num
类型。
通常,这些变量的行为与常规标量变量的行为方式相同,称为自动装箱; 然而,存在一些差异,因为您实际宣称的是如何表示它们,而不是它们的实际类型。第一个是它们的类型实际上是它们的等效类型,而不是它们的原生类型。
my int $intillo = 3;
say $intillo.^name; # OUTPUT: «Int
»
这显然意味着他们将智能匹配他们的等效(自动装箱)类型,而不是他们的原生类型:
my str $strillo = "tres";
say $strillo ~~ str; # OUTPUT: «False
»
say $strillo ~~ Str; # OUTPUT: «True
»
并且与非原生对应物不同,他们将始终具有默认值:
say (my Str $); # OUTPUT: «(Str)
»
say (my str $); # OUTPUT: «
»
say (my num $); # OUTPUT: «0
»
注意: 在 v6.c 中,num
的默认值是 NaN。
这是因为 Natives 不知道他们的类型,因为他们只是值,没有任何元数据。在多重分派 中,您可以拥有原生候选者,但无法区分相同原生类型的不同大小。也就是说,你可以有一个 Int 和 int 候选者,但是 int, atomicint, int64 等候选者之间会有歧义。
它们也不能被绑定。尝试做 my num $numillo := 3.5
会发出异常 Cannot bind to natively typed variable '$variable-name'; use assignment instead
。
原生类型也可以是复合的。
my int @intillos = ^10_000_000;
say [+] @intillos; # OUTPUT: «49999995000000
»
在这种情况下,*native*ness 扩展到复合类型,它将是 array
。
my num @many-pi = ^8 »*» π ; say @many-pi.^name; # OUTPUT: «array[num]
»
原生`数组`是 Iterable,但它们不是 List 的子类。但是,它们的行为类似于 Array; 例如,它们可以成形
my str @letter-pairs[10] = 'a'..'j' Z~ 'A'..'J';
say @letter-pairs.perl;
# OUTPUT: «array[str].new(:shape(10,), ["aA", "bB", "cC", "dD", "eE", "fF", "gG", "hH", "iI", "jJ"])
»
Types with native representation and size
关于具有原生表示的类型的提及也适用于此;它们将自动装入 Raku 类型,并且不受限制。但是,下表中列出的这些类型具有可在NativeCall函数中使用的特性:
int8 | (int8_t in C, also used for char) |
int16 | (int16_t in C, also used for short) |
int32 | (int32_t in C, also used for int) |
int64 | (int64_t in C) |
byte, uint8 | (uint8_t in C, also used for unsigned char) |
uint16 | (uint16_t in C, also used for unsigned short) |
uint32 | (uint32_t in C, also used for unsigned int) |
uint64 | (uint64_t in C) |
num32 | (float in C) |
num64 | (double in C) |
这些类型具有固定大小的表示,它独立于平台,因此可以安全地用于那些原生调用。如果我们愿意,没有什么能阻止我们在任何其他环境中使用它们。与上述类型相同,在为此类型的变量赋值时,必须考虑此大小:
my byte $intillo = 257;
say $intillo; # OUTPUT: «1
»
由于 byte
只能容纳 8 位,因此它将换行并分配模值为 256 的原始值的结果,这就是所示的内容。
声明原生大小的类型与没有声明原生大小的类型之间的主要区别是在声明中使用了 nativesize。例如,int8
以这种方式声明:
my native int8 is repr('P6int') is Int is nativesize( 8) { }
表示除了整数表示(P6int
)之外,它还将使用仅 8 位的原生大小。但是,这个特性并不打算在您的程序中使用,因为它不是 Raku 规范的一部分。
void
类型
原生 void
类型对应于 C 的 void
类型。虽然是有效类型,但您可以在表达式中使用它
use NativeCall;
my void $nothing;
say $nothing.perl; # OUTPUT: «NativeCall::Types::void
»
实际上,它是一个很难单独使用的 Uninstantiable
类型,实际上它在 link:(return) 类型中被明确禁止。但是,它通常在类型指针中找到,表示等效于 C 中的 void *
指针。
sub malloc( int32 $size --> Pointer[void] ) is native { * };
my Pointer[void] $for-malloc = malloc( 32 );
say $for-malloc.perl;
如果您需要在使用该类型的原生函数中使用它们,您还可以将 Blob nativecast 到此类指针上。
use NativeCall;
my Pointer[void] $native = nativecast(Pointer[void], Blob.new(0x22, 0x33));
但是,除此之外,它提供的功能非常有限,因为指向 void
的指针无法解引用:
use NativeCall;
my Pointer[void] $native = nativecast(Pointer[void], Buf.new(0x22, 0x33));
say $native.deref; # ERROR OUTPUT: «Internal error: unhandled target type
»
Atomic types
在这种情况下,atomic 指的是线程下的安全操作。 Raku 提供了一个类型,atomicint 和一些操作,它们共同保证了这一点。有关详细信息,请查看 link:(Numerics) 页面上的原子操作部分。
Rakudo specific native types
本节中描述的类型是特定于 Rakudo 的,因此不保证它们在其他实现中或在将来的版本中保持不变。
long | (long in C) |
longlong | (longlong in C) |
ulong | (long and unsigned in C) |
ulonglong | (longlong and unsigned in C) |
size_t | (size_t and unsigned in C) |
ssize_t | (size_t in C) |
bool | (bool in C) |
您可以像在本机 C 中使用它们一样使用它们:
use NativeCall;
my $just-an-array = CArray[int32].new( 1, 2, 3, 4, 5 );
loop ( my size_t $i = 0; $i < $just-an-array.elems; $i++ ) {
say $just-an-array[$i];
}
这将打印数组的五个元素,因为它应该是你期望的。