state of an object

Jonathan M Davis jmdavisProg at gmx.com
Mon Jul 2 10:26:29 PDT 2012


On Monday, July 02, 2012 17:25:03 Namespace wrote:
> I cannot create the Foo objects _in_ the NotNull struct because i
> need the reference of an specific object.
> And if i throw an error if the object paramter is null, it's not
> a compiler error.
> So there is no way that a null reference as paramter can be
> detect at compile time? I tried different __traits but i wondered
> that there is nothing like
> __traits(ThrowError, { object.toString(); }) which would be
> perfect to detect that cases.

The state of an object is entirely a runtime artifact. Take this function for 
example

void func(Foo foo) {}

How is the compiler going to know whether Foo is null or not? Whether foo is 
null or not is runtime state. You could have called it with func(null). You 
could have called it with func(new Foo(7)). You could have called it with 
func(bar()), and who knows what the value is that bar returns. It could be 
null. It could be Foo(42). It could be any valid value of Foo. The compiler 
can't know what the value of foo is anymore than it the sqrt function can know 
whether you're going to pass it a 0. That's all runtime-dependent.

Now that typeof(null) is its own type, it's possible to specialize on that and 
prevent a function from taking a null literal.

void func(T)(T value)
 if(is(T : Foo) && !is(T == typeof(null))
{}

or

void func()(typeof(null) value) {static assert(0;}
void func()(Foo value) {}

or in the case of a constructor

@disable this(typeof(this));

But _all_ that that prevents is null being passed directly - i.e. func(null) 
is illegal. But func(bar()) could still result in func being passed a null Foo 
if bar returns a null Foo.

Whether an object is null or not is not known until runtime and the compiler 
_cannot_ determine that for you. Object state is a runtime artifact. The only 
stuff that can be tested at compile time are types, whether a particular piece 
of code will compile or not, and the state of a particular variable which is 
created as part of CTFE. You can't test the state of variable which only 
exists at runtime. Its state doesn't exist yet!

On Monday, July 02, 2012 12:33:26 Namespace wrote:
> Can you show me an example of your two options?
> I'm not sure what do you exactly mean.

1. Throw if passed a null Foo:

struct NotNull(T)
 if(is(T == class) || isPointer!T)
{
 this(T value)
 {
 enforce(value !is null);
 }

 NotNull opAssign(NotNull rhs)
 {
 _value = rhs._value;
 }

 NotNull opAssign(Foo rhs)
 {
 enforce(value !is null);
 _value = rhs;
 }

 T get() pure nothrow
 {
 return _value;
 }

 alias get this;

private:
 T _value;
}

As an alternative to enforce, you can assert, which indicates that it's a bug 
in the program using NotNull if it ever assigns a null Foo to a NotNull!Foo, 
whereas with enforce, you're indicating that it's not a bug in the program if 
it occurs (it's just something that's considered an error when it occurs). 
With the enforce, it's perfectly okay for the program to not worry about 
assigning a null Foo to NotNull!Foo, whereas with an assertion, it needs to 
check in any case where it might occur in order to prevent passing a null Foo 
to a NotNull!Foo.

2. Don't ever construct or assign a NotNull!Foo from a Foo:

struct NotNull(T)
 if(is(T == class) || isPointer!T)
{
 this(Args...)(Args args)
 if(is(typeof(new T(args))))
 {
 _value = new T(args);
 }

 T get() pure nothrow
 {
 return _value;
 }

 alias get this;

private:
 T _value;
}


There's currently a pull request for Phobos which takes the approach of 
asserting that NotNull!Foo isn't passed a null Foo. Once it's been fixed up, 
it'll likely end up in Phobos:

https://github.com/D-Programming-Language/phobos/pull/477

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list