原文 提议添加新的__metadata 存储类,来标记函数 或聚集 声明成员字段 .
理由和动机
类型限定符 传递性地应用于所有子类型 :
struct A
{
int a;
int* b;
}
void main()
{
不变 A a;
}
但是有时 ,无论如何限定 ,特定字段 要保持可变 .如下示例: 如标准库 中数组 中RefCounted 的用法:
struct RefCounted(T)
{
T data;
size_t count;
}
struct Array(T)
{
private struct Payload
{
T[] payload;
size_t capacity;
}
private RefCounted!Payload _data;
}
void main()
{
不变 Array!(int*) arr;
}
上例,声明不变 数组.根据当前语言规则 ,引用计数 字段的_data 成员是不变 ,使得RefCounted 的count 成员,也是不变 .导致无法按全封装 方式计算不变对象 引用.
std.datetime 中Rebindable 用法:
struct Rebindable(T)
{
import std.traits : Unqual;
private union
{
T original;
Unqual!T stripped;
}
}
class TimeZone {}
struct SysTime
{
Rebindable!(不变 TimeZone) _timezoneStorage;
}
void main()
{
不变 SysTime a;
}
按不变 声明SysTime 实例时,SysTime 中可重绑定 对象也使不变 无用:可重绑定 对象的全部意义 在于绑定 到其他 . 一般按对象 一部分声明 分配器.如果上述对象 是不变 ,则分配器 无法修改其内部数据 . 以上,限定符 传递性使得很难 编写干净,封装 代码.为了缓解 这些问题,需要打破限定符传递性 的方法.因而引入__metadata :可在成员 字段的声明 中使用,从而打断传递性 .如:
struct RefCounted(T)
{
T data;
size_t count;
}
struct Array(T)
{
private struct Payload
{
T[] payload;
size_t capacity;
}
private __metadata RefCounted!Payload _data;
}
void main()
{
不变 Array!(int*) arr;
}
现在按__metadata 标记RefCounted 对象,即使对象自身是不变 ,Array 和RefCounted 内部都可修改 他们,这样达到引用计数 目的.
语义
__metadata 存储类修改字段 上类型限定符 的传播性.__metadata 只可应用于private ,可变 成员.
struct S
{
int* p;
shared int* s;
private __metadata
{
int* m;
shared int* ms;
}
__metadata int* pm;
private __metadata const int* mc;
private __metadata immutable int* imc;
}
除了__metadata 字段,不变 和常 仍是可传递 的.修改__metadata 字段,是已定义 的行为.
struct S
{
int p;
private __metadata int m;
void increment() immutable
{
++m;
++p;
}
}
__metadata 不应用传递性 .
struct RefCount
{
immutable size_t id;
size_t count;
}
struct T
{
private __metadata RefCount s;
void updateRC() immutable
{
++s.count;
++s.id;
}
}
限制
全局/局部和静态变量 不能是__metadata :
module jaja;
__metadata int x = 2;
int fun()
{
__metadata int y;
}
struct S
{
private __metadata static int b;
}
只能在@system 代码中操作__metadata 数据:
struct S
{
private __metadata int* m;
}
int* bar() @safe
{
S s;
return s.m;
}
shared
因为不变 是隐式共享 的,为了线程安全 ,按shared 对待不变 实例的__metadata 字段:
struct S
{
private __metadata int* p;
}
immutable S s;
static assert(is(typeof(s.p) == shared(int*)));
可从可变 ,const 和不变 对象得到const 对象,因此类型系统 无法推导对象内__metadata 字段应为共享或不共享 .按非共享 可变类型检查 ,const 对象的__metadata 字段,让用户理解 代码并正确的强制转换 :
shared int* q;
int* r;
struct A
{
private __metadata int* p;
bool isImmutable;
this(shared int *p) immutable
{
this.p = p;
isImmutable = true;
}
void fun() const
{
pragma(msg, typeof(p).stringof);
if (isImmutable)
q = cast(shared int*) p;
else
r = p;
}
}
void main()
{
shared int* p
immutable A ia = immutable A(p);
const A ca;
ia.fun();
ca.fun();
}
可从可变 ,const 和不变 对象得到inout 对象 .__metadata 字段的inout 属性按常 一样类似 对待.
pure
添加__metadata 后,不变 实例,按函数参数传递时,不会提供相同 的纯度保证 :
struct S
{
private __metadata int* p;
void inc() immutable pure
{
*p += 1;
}
}
void foo(ref immutable S a) pure
{
a.inc();
}
int x;
void main()
{
A b = A(&x);
foo(b);
}
此DIP 之前,按强纯 函数对待foo ,但添加__metadata 后,现在可通过元数据 字段修改 实例,因此foo 成为弱纯 函数.该DIP 之后,仅当参数不包含元数据 字段时,才按强纯 函数对待 带不变ref/pointer 参数的函数.
|