www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - state of an object

reply "Namespace" <rswhite4 googlemail.com> writes:
Is it possible to figure out how is the state of an object at 
compile time?
E.g. if the object is null or not:

class Foo { }

Foo f;

static if (is_null(f)) { }
Jul 01 2012
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Namespace:

 Is it possible to figure out how is the state of an object at 
 compile time?
 E.g. if the object is null or not:

 class Foo { }

 Foo f;

 static if (is_null(f)) { }

In general you need a tool that analyzes D code statically (and maybe in some cases doesn't give a certain answer). Bye, bearophile
Jul 01 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 In general you need a tool that analyzes D code statically (and 
 maybe in some cases doesn't give a certain answer).

 Bye,
 bearophile

Short: not so easy. Too bad.
Jul 02 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, July 02, 2012 08:19:52 Namespace wrote:
 Is it possible to figure out how is the state of an object at
 compile time?
 E.g. if the object is null or not:
 
 class Foo { }
 
 Foo f;
 
 static if (is_null(f)) { }

What are you trying to test exactly? Whether f default initializes to null? That's as easy as static if(typeof(f).init is null); But if what you want to test is what f is initialized to - such that you can get the value if it were Foo f = initialize(); then no, you can't do that. D won't let you use any global or static variable at compile time in order to avoid subtle errors caused by ordering and circular dependencies. If f were an enum you could, but then it's a manifest constant rather than a variable, and you can't currenly initialize classes to anything other than null at compile time. - Jonathan M Davis
Jul 02 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
My intention was to avoid something like this:

[code]
class Foo { }

Foo f; // f is null

NotNull!(Foo) test = f; // should throw an compiler error, 
because f is null
[/code]

I can avoid

NotNull!(Foo) test = null; with

 disable
this(typeof(null));

but how can i avoid null objects?
Jul 02 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, July 02, 2012 10:41:03 Namespace wrote:
 My intention was to avoid something like this:
 
 [code]
 class Foo { }
 
 Foo f; // f is null
 
 NotNull!(Foo) test = f; // should throw an compiler error,
 because f is null
 [/code]
 
 I can avoid
 
 NotNull!(Foo) test = null; with
 
  disable
 this(typeof(null));
 
 but how can i avoid null objects?

The _only_ way to statically disallow assigning a null Foo to a NotNull!Foo object is to statically disallow assigning a Foo to it period. Its nullity is not part of its type and therefore cannot be checked statically. As far as I can tell, you have two choices: 1. Throw when a NotNull is assigned or constructed from a null Foo. 2. Disallow assignment and construction of a NotNull!Foo from any and all Foos - in which case the NotNull!Foo needs to construct any Foo that it contains. - Jonathan M Davis
Jul 02 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
Can you show me an example of your two options?
I'm not sure what do you exactly mean.
Jul 02 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
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.
Jul 02 2012
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
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
Jul 02 2012