2014-02-05 7 views
3
난 단지 생성자를 사용하여 NotNull 기본 클래스 인스턴스에 NotNull 상속 된 클래스 인스턴스의 할당을 허용하는 아담 D Ruppes 모듈 notnull.d 조금 변경했습니다

가 NOTNULL 오른쪽을 얻기

/** Assignment from $(D NotNull) Inherited Class $(D rhs) to $(D NotNull) Base 
    Class $(D this). */ 
typeof(this) opAssign(U)(NotNull!U rhs) @safe pure nothrow if (isAssignable!(T, U)) { 
    this._value = rhs._value; 
    return this; 
} 

unittest 
{ 
    class A {} 
    class B : A {} 
    NotNull!B b = assumeNotNull(new B); 
    NotNull!A a = assumeNotNull(new A); 
    a = b; 
    assert(a is b); 
} 
수 있습니다

그러나 허용 할 방법을 찾지 못했습니다.

unittest 
{ 
    class A {} 
    class B : A {} 
    void f(NotNull!A a) {} 
    NotNull!B b = assumeNotNull(new B); 
    f(b); 
} 

내가 자동으로 작동해야한다고 생각하는 컴파일.

alias this은 분명히이 작업에 충분하지 않습니다.

어떻게하면됩니까?

도 참조 : 모듈 notnull.dhttp://forum.dlang.org/thread/[email protected]#post-aprsozwvnpnchbaswjxd:40forum.dlang.org

의 전체 소스는 다음과 같습니다

나에게로
#!/usr/bin/env rdmd-dev-module 

module notnull; 

import std.traits: isAssignable; 

/** Note that NotNull!T is not NotNullable :) */ 
alias NotNullable(T) = isAssignable!(T, typeof(null)); 

/** 
    NotNull ensures a null value can never be stored. 

    * You must initialize it when declared 

    * You must never assign the null literal to it (this is a compile time error) 

    * If you assign a null value at runtime to it, it will immediately throw an Error 
    at the point of assignment. 

    NotNull!T can be substituted for T at any time, but T cannot become 
    NotNull without some attention: either declaring NotNull!T, or using 
    the convenience function, notNull. 

    Condition: T must be a reference type. 
    Instead of: __traits(compiles, { T t; assert(t is null); }. 

    TODO: Merge with http://arsdnet.net/dcode/notnullsimplified.d 

    Examples: 
    --- 
    int myInt; 
    NotNull!(int *) not_null = &myInt; 
    // you can now use variable not_null anywhere you would 
    // have used a regular int*, but with the assurance that 
    // it never stored null. 
    --- 
*/ 
struct NotNull(T) if (NotNullable!T) 
{ 
    @disable this(); // Disallow default initialized (to null) 

    /** Assignment from $(D NotNull) Inherited Class $(D rhs) to $(D NotNull) Base 
     Class $(D this). */ 
    typeof(this) opAssign(U)(NotNull!U rhs) @safe pure nothrow if (isAssignable!(T, U)) { 
     this._value = rhs._value; 
     return this; 
    } 

    NotNull!U opCast(U)() @safe pure nothrow if (isAssignable!(U, T)) { 
     return NotNull!_value; 
    } 

    // this could arguably break the static type check because 
    // you can assign it from a variable that is null.. but I 
    // think it is important that NotNull!Object = new Object(); 
    // works, without having to say assumeNotNull(new Object()) 
    // for convenience of using with local variables. 

    /// Constructs with a runtime not null check (via assert()). 
    this(T value) @safe pure nothrow 
    { 
     assert(value !is null); 
     _value = value; 
    } 

    /** Disable null construction. */ 
    @disable this(typeof(null)); 
    /** Disable null assignment. */ 
    @disable typeof(this) opAssign(typeof(null)); 

    private T _value; 
    @property inout(T) _valueHelper() inout 
    { 
     assert(_value !is null); // sanity check of invariant 
     return _value; 
    } 
    // Apparently a compiler bug - the invariant being uncommented breaks all kinds of stuff. 
    // invariant() { assert(_value !is null); } 

    alias _valueHelper this; /// this is substitutable for the regular (nullable) type 

    /* void toMsgpack (Packer) (ref Packer packer) const { packer.pack(_value); } */ 
    /* void fromMsgpack(Unpacker)(auto ref Unpacker unpacker) { unpacker.unpack(_value); } */ 
} 

/** A convenience function to construct a NotNull value from something $(D t) 
    you know isn't null. 
*/ 
NotNull!T assumeNotNull(T)(T t) if (NotNullable!T) 
{ 
    return NotNull!T(t); // note the constructor asserts it is not null 
} 

/** A convenience function to check for null $(D t). If you pass null to $(D t), 
    it will throw an exception. Otherwise, return NotNull!T. 
*/ 
NotNull!T enforceNotNull(T, string file = __FILE__, size_t line = __LINE__)(T t) if (NotNullable!T) 
{ 
    import std.exception: enforce; 
    enforce(t !is null, "t is null!", file, line); 
    return NotNull!T(t); 
} 

unittest 
{ 
    import core.exception; 
    import std.exception; 

    void NotNullCompiliationTest1()() // I'm making these templates to defer compiling them 
    { 
     NotNull!(int*) defaultInitiliation; // should fail because this would be null otherwise 
    } 
    assert(!__traits(compiles, NotNullCompiliationTest1!()())); 

    void NotNullCompiliationTest2()() 
    { 
     NotNull!(int*) defaultInitiliation = null; // should fail here too at compile time 
    } 
    assert(!__traits(compiles, NotNullCompiliationTest2!()())); 

    int dummy; 
    NotNull!(int*) foo = &dummy; 

    assert(!__traits(compiles, foo = null)); // again, literal null is caught at compile time 

    int* test; 

    test = &dummy; 

    foo = assumeNotNull(test); // should be fine 

    void bar(int* a) {} 

    // these should both compile, since NotNull!T is a subtype of T 
    bar(test); 
    bar(foo); 

    void takesNotNull(NotNull!(int*) a) { } 

    assert(!__traits(compiles, takesNotNull(test))); // should not work; plain int might be null 
    takesNotNull(foo); // should be fine 

    takesNotNull(assumeNotNull(test)); // this should work too 
    assert(!__traits(compiles, takesNotNull(assumeNotNull(null)))); // notNull(null) shouldn't compile 
    test = null; // reset our pointer 

    assertThrown!AssertError(takesNotNull(assumeNotNull(test))); // test is null now, so this should throw an assert failure 

    void takesConstNotNull(in NotNull!(int *) a) {} 

    test = &dummy; // make it valid again 
    takesConstNotNull(assumeNotNull(test)); // should Just Work 

    NotNull!(int*) foo2 = foo; // we should be able to assign NotNull to other NotNulls too 
    foo2 = foo; // including init and assignment 

} 

unittest 
{ 
    class A {} 
    class B : A {} 
    NotNull!B b = assumeNotNull(new B); 
    NotNull!A a = assumeNotNull(new A); 
    a = b; 
    assert(a is b); 
} 
+1

나는 오늘 D에서 완전히 올바른 얻을 수 있습니다 생각하지 않습니다. 각 기본 클래스의 별칭을 시도한 다음 파생 클래스의 함수에 액세스하기 위해 opDispatch 또는 opDot를 추가 할 수 있습니다 (별칭이 기본 클래스를 가리키고 있기 때문에 별칭이 작동하지 않으므로 모든 클래스를 포함하지는 않습니다). 인터페이스. 어쩌면 여러 별칭이 구현 될 때 (숨을 멈추지 않고 오랫동안 기다렸지 만) 그렇지 않으면 블레이. 어쩌면 우리는 많은 포장 된 유형을 없애기 때문에 새로운 기능을 요구해야합니다. –

+0

동의합니다. 이러한 포장 된 유형을 만드는 것은 중요한 문제입니다. 이것은 2.066에 대한 나의 가장 큰 소원이다. 이 요청을 어딘가에 보내야합니까? –

답변

0

NOTNULL 그냥 정확히 하나 개의 값에 대한 용기와 공변/contravariant 때문에,이 나쁜 생각입니다 컨테이너는 런타임 무결성에 대해 안전하지 않습니다. 의이 아이디어가 구현되는 상상하자이 약간 변형 예 고려 : 한 개체에서 호출)

unittest 
{ 
    class A {} 
    class B : A { void bb(){} } 

    void f(ref NotNull!A a) { 
     a = assumeNotNull(new A); 
    } 

    NotNull!B b = assumeNotNull(new B); 
    f(b); 
    b.bb(); 
} 

b.bb을 (그리고 f는() 다른 모듈에있는 경우 다음 컴파일러는이 잡을 수있는 기회가 없습니다 발행물.

공변으로 만드는 유일한 방법은 실제로 모든 불변 메소드 (opAssign, fromMsgpack)를 제거하는 것입니다.

추가 읽기 : Variance in C++, Cheat Codes

관련 문제