2.28. 泛型编程
Daslang 允许在语句、函数和函数声明中省略类型,使其类似于动态类型语言(如 Python 或 Lua)的编写。 所述函数在第一次调用时针对特定类型的参数 * 实例化* 。
还有一些方法可以检查提供的参数的类型,以便更改函数的行为,或在编译阶段提供合理有意义的错误。 这些方法中的大多数都是通过 s 实现的
与 C++ 及其 SFINAE 不同,您可以使用通用条件 (if) 来根据其参数的类型信息来更改函数的实例。 请考虑以下示例:
def setSomeField(var obj; val) {
if ( typeinfo has_field<someField>(obj) ) {
obj.someField = val
}
}
这个函数在提供的参数中设置 someField
如果它是一个带有 someField
成员的结构体。
我们可以做得更多。 例如:
def setSomeField(var obj; val: auto(valT))
if ( typeinfo has_field<someField>(obj) ) {
if ( typeinfo typename(obj.someField) == typeinfo typename(type<valT -const>) ) {
obj.someField = val
}
}
这个函数在提供的参数中设置 someField
如果它是一个带有 someField
成员的结构体,并且只有当 someField
和 val
是同一类型时!
2.28.1. typeinfo
大多数类型反射机制都是通过 typeinfo 运算符实现的。有:
typeinfo typename(object)
// 返回 Object 的 typename
typeinfo fulltypename(object)
// 返回 Object 的完整 typename,其中包含 Contract (如 !const, 或 !&)
typeinfo sizeof(object)
// 返回 sizeof
typeinfo is_pod(object)
// 如果对象为 POD 类型,则返回 true
typeinfo is_raw(object)
// 如果对象是原始数据,即可以使用 memcpy 复制,则返回 true
typeinfo is_struct(object)
// 如果 object 为 struct,则返回 true
typeinfo has_field<name_of_field>(object)
// 如果 object 是字段为 name_of_field 的结构,则返回 true
typeinfo is_ref(object)
// 如果 object 是对某物的引用,则返回 true
typeinfo is_ref_type(object)
// 如果 Object 是引用类型(例如 Array、Table、das_string 或其他已处理的引用类型),则返回 true
typeinfo is_const(object)
// 如果 object 是 const 类型(即无法修改),则返回 true
typeinfo is_pointer(object)
// 如果 Object 是指针类型,即 int?
所有 typeinfo 都可以使用 type
关键字处理类型,而不是对象:
typeinfo typename (type<int>) // 返回 "int"
2.28.2. auto 和 auto(named)
在泛型中不必省略类型名称,而是可以使用显式的 auto
类型或 auto(name)
来键入它:
def fn(a: auto): auto {
return a
}
或
def fn(a: auto(some_name)): some_name {
return a
}
这与以下相同:
def fn(a) {
return a
}
如果函数接受大量参数,并且其中一些参数必须属于同一类型,这将非常有用:
def fn(a, b) { // a 和 b 可以是不同的类型
return a + b
}
这与以下不同:
def fn(a, b: auto) { // a 和 b 是一种类型
return a + b
}
此外,请考虑以下事项:
def set0(a, b; index: int) { // a 只应该是数组类型,与 b 的类型相同
return a[index] = b
}
如果使用 floats 数组和 int 调用此函数,则会收到不太明显的编译器错误消息:
def set0(a: array<auto(some)>; b: some; index: int) { // a 是数组类型,与 b 类型相同
return a[index] = b
}
命名 auto
与 typeinfo
的用法
def fn(a: auto(some)) {
print(typeinfo typename(type<some>))
}
fn(1) // 打印 "const int"
您还可以使用 delete 语法修改类型:
def fn(a: auto(some)) {
print(typeinfo typename(type<some -const>))
}
fn(1) // 打印 "int"
2.28.3. 类型协定和类型操作
泛型函数参数、result 和推断的类型别名可以在推理期间进行作。
const 指定常量和正则表达式将匹配:
def foo ( a : Foo const ) // 接受 Foo 和 Foo const
==const 指定表达式的 const 必须与参数的 const 匹配:
def foo ( a : Foo const ==const ) // 仅接受 Foo const
def foo ( var a : Foo ==const ) // 仅接受 Foo
-const 将从匹配类型中删除 const:
def foo ( a : array<auto -const> ) // 匹配任何具有 non-const 元素的数组
# 指定只接受临时类型:
def foo ( a : Foo# ) // 仅接受 Foo#
-# 将从匹配类型中删除临时类型:
def foo ( a : auto(TT) ) { // 接受任何类型
var temp : TT -# := a // TT -# 现在是常规类型,当 `a` 是临时的时,它可以将其克隆到 `temp` 中
}
& 指定参数通过引用传递:
def foo ( a : auto& ) // 接受通过引用传递的任何类型的
==& 指定表达式的引用必须与参数的引用匹配:
def foo ( a : auto& ==& ) // 接受通过引用传递的任何类型的 (例如,变量 i,即使其整数)
def foo ( a : auto ==& ) // 接受按值传递的任意类型(例如值 3)
-& 将从匹配类型中删除引用:
def foo ( a : auto(TT)& ) { // 接受通过引用传递的任何类型的
var temp : TT -& = a // TT -& 不是本地引用
}
[] 指定参数是任意维度的静态数组:
def foo ( a : auto[] ) // 接受任何类型、任何大小的静态数组
-[] 将从匹配类型中删除静态数组维度:
def take_dim( a : auto(TT) ) {
var temp : TT -[] // temp is type of element of a
}
// 如果 a 为 int[10],则 temp 为 int
// 如果 a 为 int[10][20][30],则 temp 仍为 int
implicit 指定临时类型和常规类型都可以匹配,但类型将被视为指定类型。`implicit`是 _UNSAFE_:
def foo ( a : Foo implicit ) // 接受 Foo 和 Foo#,则 a 将被视为 Foo
def foo ( a : Foo# implicit ) // 接受 Foo 和 Foo#,则 a 将被视为 Foo#
`explicit`指定不应用 LSP,只接受完全类型匹配:
def foo ( a : Foo ) // 接受 Foo 和直接或间接从 Foo 继承的任何类型
def foo ( a : Foo explicit ) // 仅接受 Foo
2.28.4. 选项
可以将多个选项指定为函数参数:
def foo ( a : int | float ) // 接受 int 或 float
可选类型始终使函数成为泛型。
泛型选项将按列出的顺序进行匹配:
def foo ( a : Bar explicit | Foo ) // first 将尝试精确匹配 Bar,而不是从 Foo 继承的任何其他内容
|# shortcat 匹配以前的类型,临时翻转:
def foo ( a : Foo |# ) // 按顺序接受 Foo 和 Foo#
def foo ( a : Foo# |# ) // 按该顺序接受 Foo# 和 Foo
2.28.5. typedecl
请考虑以下示例:
struct A {
id : string
}
struct B {
id : int
}
def get_table_from_id(t : auto(T)) {
var tab : table<typedecl(t.id); T> // 注意 typedecl
return <- tab
}
[export]
def main {
var a : A
var b : B
var aTable <- get_table_from_id(a)
var bTable <- get_table_from_id(b)
print("{typeinfo typename(aTable)}\n")
print("{typeinfo typename(bTable)}\n")
}
此处使用 provided struct 的 id 字段的键类型创建 table。 此功能允许根据提供的表达式类型创建类型。
2.28.6. 泛型元组和类型<>表达式
Consider the following example:
tuple Handle {
h : auto(HandleType)
i : int
}
def make_handle ( t : auto(HandleType) ) : Handle {
var h : type<Handle> // 注意 type<Handle>
return h
}
def take_handle ( h : Handle ) {
print("count = {h.i} of type {typeinfo typename(type<HandleType>)}\n")
}
[export]
def main {
let h = make_handle(10)
take_handle(h)
}
在函数 make_handle 中,变量 h 的类型是使用 type<> 表达式创建的。 type<> 在 context 中推断(这次基于函数参数)。 此功能允许根据提供的表达式类型创建类型。
泛型函数 take_handle 采用任何 Handle 类型,但只接受 Handle 类型元组。
这与 C++ 模板系统有一些相似之处,但由于元组是弱类型,因此受到更多限制。