d安全引用计数
原文
 要加个@trusted包装器,才能同域一起用.
 析构器检查dip1000,否则保持@system.
import std.stdio;
import std.typecons;
struct Container
{
    ubyte[] data;
}
struct Blob
{
    ubyte[64] data;
}
void main ()
{
    auto ptr = getPtr();
    writeln(ptr);
    const save = ptr.dup;
    auto other = getPtr();
    assert(save == ptr);
}
ubyte[] getPtr ()
{
    Blob blob;
    RefCounted!Container rc;
    escape(rc.refCountedPayload(), blob);
    assert(rc.refCountedPayload().data !is null);
    return rc.refCountedPayload().data;
}
void escape (ref Container rc, ref Blob local)
{
    rc.data = local.data;
}
编译,运行,崩溃.
 不过,更像是编译器错误而不是RefCounted错误
 是的,可滥用:
@safe unittest //编译
{
    void escape (ref ubyte[] arr, ref ubyte[64] local)
    {
        arr = local;
    }
    @safe ubyte[] getPtr ()
    {
        ubyte[64] blob;
        ubyte[] arr;
        escape(arr, blob);
        return arr; // 就像c时代.
    }
}
opAssign按值,而非按引用.
准备实现侵改:
@safe unittest
{
    @safe void assignCrap(RefCounted!(int*) countedRef)
    {
        int local;
        auto anotherRef = countedRef;
//anotherRef生命期更短.
        anotherRef.refCountedPayload() = &local; // ...这样,这会编译
    }
}
编译器未实现,可能refCountedPayload中的return属性,使得,生命期(负载)<生命期(anotherRef).
 可能,构+成员函数可侵改.
这是有问题的:
@safe unittest
{
    auto rc = RefCounted!int(123);
    (ref int n) {
        destroy(rc);
        int oops = n; // 释放后使用.
    }(rc.refCountedPayload);
}
为确保不在@safe中发生,refCountedPayload和析构器必须是@system.
简单变体:
@safe unittest
{
    auto rc = RefCounted!int(123);
    int* ptr = &rc.refCountedPayload();
    destroy(rc);
    int oops = *ptr;
}
问题是,允许域前析构rc.只能在生命期性调用析构器算@safe,其余不应@safe.在@live中可安全,其他则不是.
 对-dip1000也会:
@safe void abuse(Container)()
{ auto cont = Container([1,2,3]);
  scope ptr = &cont.front;
  destroy(cont);
  int oops = *ptr;
}
可基于回调的api
auto ref apply(alias callback, T)(auto ref RefCounted!T this_)
{
    //确保在`callback`返回前保持引用
    auto hold = this_;
    //显式`域`防止`callback`逃逸引用
    scope ptr = () @trusted { return &this_.refCountedPayload(); }();
    return callback(*ptr);
}
// Usage
@safe unittest
{
    auto rc = RefCounted!int(123);
    rc.apply!((ref n) {
        destroy(rc);
        int ok = n; // 仍活着.
    });
}
缺点是:1,语法不好.2,额外引用计数.
 在真正所有权/借用前(@live不算),提供2个版本:
 @safe为愿意接受运行时成本用户提供基于回调的API,及@system提供直接访问的API,并让用户有责任不创建悬空引用.
 即,使refCountedPayload为@system,加个@safe的apply.
 有人destroy了两次,怎么办?
析构器应重置_refCounted为RefCountedStore.init,确保即使使用destroy!false,它也是幂等的.
让refCountedPayload在@safe中工作,析构器为私有,并定义noEarlyDestroy属性,
 对注解为noEarlyDestroy的destroy应为@system.
 有人提出.
 由于应阻止安全访问私字段而绕过.
__traits(getMember)破坏@safe的所有东西,可以假装它不存在.
 __traits(getMember)很难修复,也是@system变量动机,用来阻塞@safe访问不依赖private的代码.
 我们不应让@安全破坏私.同时,不应加依赖私的内存安全特征.
DIP1035可解决大部分__traits(getmember),或许@safe访问私会成为特性而不是漏洞.
 不一致,在Nullable上与RefCounted上用apply,前者不做啥,后者初化并触发断定,不一致.
重命名更好.因为两个版本处理回调返回值方式不同.
rc.borrow!((scope ref payload) { /* ... */ });
rc.withPayload!((scope ref payload) { /* ... */ });
borrow不错.
 用-preivew=dip1000编译.
 发现问题:
 虽然确实可使dirEntries为模板来解决链接问题,即使模板化它和或添加析构器,但DirIterator有些生成析构器不会消失.尝试链接的符号在后者中变化了,仅此而已.目前在找原因.
struct _DirIteratorTempl()
{
  //实现
}
alias DirIterator = _DirIteratorTempl!();
auto dirEntries()(string path, SpanMode mode, bool followSymlink = true)
{
  //工作,无链接错误.
  return _DirIteratorTempl!()(path, mode, followSymlink);
}
auto dirEntries()(string path, SpanMode mode, bool followSymlink = true)
{
    //报错,好像_DirIteratorTempl不是模板!
  return DirIterator(path, mode, followSymlink);
}
未记录DirIterator,可修改,算运气了.
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号