.. _generic_programming: =================== 泛型编程 =================== .. index:: single: Generic Programming Daslang 允许在语句、函数和函数声明中省略类型,使其类似于动态类型语言(如 Python 或 Lua)的编写。 所述函数在第一次调用时针对特定类型的参数 * 实例化* 。 还有一些方法可以检查提供的参数的类型,以便更改函数的行为,或在编译阶段提供合理有意义的错误。 这些方法中的大多数都是通过 s 实现的 与 C++ 及其 SFINAE 不同,您可以使用通用条件 (if) 来根据其参数的类型信息来更改函数的实例。 请考虑以下示例:: def setSomeField(var obj; val) { if ( typeinfo has_field(obj) ) { obj.someField = val } } 这个函数在提供的参数中设置 ``someField`` 如果它是一个带有 ``someField`` 成员的结构体。 我们可以做得更多。 例如:: def setSomeField(var obj; val: auto(valT)) if ( typeinfo has_field(obj) ) { if ( typeinfo typename(obj.someField) == typeinfo typename(type) ) { obj.someField = val } } 这个函数在提供的参数中设置 ``someField`` 如果它是一个带有 ``someField`` 成员的结构体,并且只有当 ``someField`` 和 ``val`` 是同一类型时! ^^^^^^^^^ 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(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" ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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; b: some; index: int) { // a 是数组类型,与 b 类型相同 return a[index] = b } 命名 ``auto`` 与 ``typeinfo`` 的用法 :: def fn(a: auto(some)) { print(typeinfo typename(type)) } fn(1) // 打印 "const int" 您还可以使用 delete 语法修改类型:: def fn(a: auto(some)) { print(typeinfo typename(type)) } fn(1) // 打印 "int" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 类型协定和类型操作 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 泛型函数参数、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 ) // 匹配任何具有 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 ^^^^^^^ 选项 ^^^^^^^ 可以将多个选项指定为函数参数:: 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 ^^^^^^^^ typedecl ^^^^^^^^ 请考虑以下示例:: struct A { id : string } struct B { id : int } def get_table_from_id(t : auto(T)) { var tab : table // 注意 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。 此功能允许根据提供的表达式类型创建类型。 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 泛型元组和类型<>表达式 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Consider the following example:: tuple Handle { h : auto(HandleType) i : int } def make_handle ( t : auto(HandleType) ) : Handle { var h : type // 注意 type return h } def take_handle ( h : Handle ) { print("count = {h.i} of type {typeinfo typename(type)}\n") } [export] def main { let h = make_handle(10) take_handle(h) } 在函数 make_handle 中,变量 h 的类型是使用 type<> 表达式创建的。 type<> 在 context 中推断(这次基于函数参数)。 此功能允许根据提供的表达式类型创建类型。 泛型函数 take_handle 采用任何 Handle 类型,但只接受 Handle 类型元组。 这与 C++ 模板系统有一些相似之处,但由于元组是弱类型,因此受到更多限制。