www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - String to boolean inconsistency

reply =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= <just ask.me> writes:
string s = "";
assert(s);  // ok
assert(s != null);  // fails

I guess that's a bug. But which one is right?

--
Tomek
Dec 11 2010
next sibling parent reply Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
I forget, why are we supposed to use is instead of == with null?

On 12/11/2010 08:18 PM, Tomek Sowiński wrote:
 string s = "";
 assert(s); // ok
 assert(s != null); // fails

 I guess that's a bug. But which one is right?

 --
 Tomek

Dec 11 2010
next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Ellery Newcomer <ellery-newcomer utulsa.edu> wrote:

 I forget, why are we supposed to use is instead of == with null?

'[] is null' compares ptr and length, while '[] == null' compares only the length. Weirdly though, '[] is null' is false for ptr == 0, length != 0. Not likely to happen much in practice. -- Simen
Dec 11 2010
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Simen kjaeraas <simen.kjaras gmail.com> wrote:

 Ellery Newcomer <ellery-newcomer utulsa.edu> wrote:

 I forget, why are we supposed to use is instead of == with null?

'[] is null' compares ptr and length, while '[] == null' compares only the length. Weirdly though, '[] is null' is false for ptr == 0, length != 0. Not likely to happen much in practice.

To actually answer your question, == calls opEquals for classes, and that is called on both lhs and rhs. null.opEquals however, segfaults. is, on the other hand, compares pointer equality, without caring for the details of what opEquals wants to do. -- Simen
Dec 11 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
The first one should fail since the string has length 0. If you use an
.idup you would get the correct results:

void main()
{
string s =3D "".idup;
assert(s);  // fails
assert(s !=3D null);  // ok
}

So I guess it's a bug.

On 12/12/10, Tomek Sowi=F1ski <just ask.me> wrote:
 string s =3D "";
 assert(s);  // ok
 assert(s !=3D null);  // fails

 I guess that's a bug. But which one is right?

 --
 Tomek

Dec 11 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Actually I'm having a hard time understanding this:

void main()
{
string s =3D "";
assert(s);  // pass, but why?
assert(s !is null);  // pass
}

void main()
{
string s =3D "".idup;
assert(s);  // fail
assert(s !is null);  // pass
}

On 12/12/10, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:
 The first one should fail since the string has length 0. If you use an
 .idup you would get the correct results:

 void main()
 {
 string s =3D "".idup;
 assert(s);  // fails
 assert(s !=3D null);  // ok
 }

 So I guess it's a bug.

 On 12/12/10, Tomek Sowi=F1ski <just ask.me> wrote:
 string s =3D "";
 assert(s);  // ok
 assert(s !=3D null);  // fails

 I guess that's a bug. But which one is right?

 --
 Tomek


Dec 11 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:

 Actually I'm having a hard time understanding this:

 void main()
 {
 string s = "";
 assert(s);  // pass, but why?
 assert(s !is null);  // pass
 }

 void main()
 {
 string s = "".idup;
 assert(s);  // fail
 assert(s !is null);  // pass
 }

Try adding writeln( s.ptr ); in there. Should probably give you an indication of what is and == do. Answer: == null: Length P ==0 !=0 t ==0 T F r !=0 T F is null: Length P ==0 !=0 t ==0 T F r !=0 F F Where T and F stand for true and false, respectively. So likely, idup on an empty string returns an array with null ptr and 0 length, while "" is 'allocated' in the data segment, and thus given a ptr value. -- Simen
Dec 11 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 11 December 2010 18:18:54 Tomek Sowi=F1ski wrote:
 string s =3D "";
 assert(s);  // ok
 assert(s !=3D null);  // fails
=20
 I guess that's a bug. But which one is right?

A null array and an empty array are essentially the same thing as far as D = is=20 concerned. I believe that the only difference is whether "is null" returns = true=20 or not. So, this program import std.range; import std.stdio; void main() { string s =3D ""; writefln("[%s]", s); writeln(s is null); writeln(s =3D=3D null); writeln(s.empty); writeln(); string t =3D null; writefln("[%s]", t); writeln(t is null); writeln(t =3D=3D null); writeln(t.empty); } prints [] false true true [] true true true "=3D=3D" is _not_ the correct operator to use to check for null anyway. "i= s" is the=20 correct operator for that. This behavior is intended. Arrays are actually something like this under th= e=20 hood: struct array(T) { T* arr; size_t length; } By declaring an array to be null, all you're doing is setting arr to null.= =20 std.range.empty() will check length and ignore arr. "=3D=3D" will presumabl= y only=20 check arr if the length of the two arrays is the same. If they are the same= ,=20 then each element is checked fore equality. But if their length is zero, th= en=20 you don't need to check any elements. By doing something like assert("" =3D=3D null) you're likely creating an array which has whose arr value is non-null (but = it's=20 not actually part of the array; the memory there is extra capacity which wi= ll be=20 used if/when you append to the array; it avoids extra memory re-allocations= that=20 way) and an array whose arr value is null. Both will have their lengths be = 0. When "is null" is used, it checks that arr is null. If "=3D=3D" is used, it= checks=20 whether the elements of the two arrays are the same. Since they have the sa= me=20 length, that would mean iterating through all of their elements to verify t= hat=20 each element is the same. But since there are no elements in either, no ele= ments=20 get checked. The fact that the "" array has extra capacity is irrelevant. I= t's=20 not part of the array, just arr. So, the fact that assert("" !=3D null); fails is intended. It's a result of how arrays are designed. =2D Jonathan M Davis
Dec 11 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Jonathan M Davis <jmdavisProg gmx.com> wrote:

 This behavior is intended. Arrays are actually something like this under  
 the hood:

 struct array(T)
 {
     T* arr;
     size_t length;
 }

Actually, that is: struct array(T) { size_t length; T* ptr; } To get the layout and names right. ( http://digitalmars.com/d/2.0/abi.html ) -- Simen
Dec 11 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 11 December 2010 19:00:55 Jonathan M Davis wrote:
 On Saturday 11 December 2010 18:18:54 Tomek Sowi=F1ski wrote:
 string s =3D "";
 assert(s);  // ok
 assert(s !=3D null);  // fails
=20
 I guess that's a bug. But which one is right?

A null array and an empty array are essentially the same thing as far as D is concerned. I believe that the only difference is whether "is null" returns true or not. So, this program =20 import std.range; import std.stdio; =20 void main() { string s =3D ""; =20 writefln("[%s]", s); writeln(s is null); writeln(s =3D=3D null); writeln(s.empty); writeln(); =20 string t =3D null; =20 writefln("[%s]", t); writeln(t is null); writeln(t =3D=3D null); writeln(t.empty); } =20 =20 prints =20 [] false true true =20 [] true true true =20 =20 "=3D=3D" is _not_ the correct operator to use to check for null anyway. =

 is the correct operator for that.
=20
 This behavior is intended. Arrays are actually something like this under
 the hood:
=20
 struct array(T)
 {
     T* arr;
     size_t length;
 }
=20
=20
 By declaring an array to be null, all you're doing is setting arr to null.
 std.range.empty() will check length and ignore arr. "=3D=3D" will presuma=

 only check arr if the length of the two arrays is the same. If they are
 the same, then each element is checked fore equality. But if their length
 is zero, then you don't need to check any elements. By doing something
 like
=20
 assert("" =3D=3D null)
=20
 you're likely creating an array which has whose arr value is non-null (but
 it's not actually part of the array; the memory there is extra capacity
 which will be used if/when you append to the array; it avoids extra memory
 re-allocations that way) and an array whose arr value is null. Both will
 have their lengths be 0.
=20
 When "is null" is used, it checks that arr is null. If "=3D=3D" is used, =

 checks whether the elements of the two arrays are the same. Since they
 have the same length, that would mean iterating through all of their
 elements to verify that each element is the same. But since there are no
 elements in either, no elements get checked. The fact that the "" array
 has extra capacity is irrelevant. It's not part of the array, just arr.
 So, the fact that
=20
 assert("" !=3D null);
=20
 fails is intended. It's a result of how arrays are designed.

On reflection, I should have used the name ptr instead of array, since that= _is_=20 its actual name. It slipped my mind I guess... =2D Jonathan M Davis
Dec 11 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 11 December 2010 19:08:33 Simen kjaeraas wrote:
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 This behavior is intended. Arrays are actually something like this under
 the hood:
 
 struct array(T)
 {
 
     T* arr;
     size_t length;
 
 }

Actually, that is: struct array(T) { size_t length; T* ptr; } To get the layout and names right. ( http://digitalmars.com/d/2.0/abi.html )

Good point. Though actually, I don't think that that's quite right either, because it has capacity as well. It seems like there's an error in the documentation. - Jonathan M Davis
Dec 11 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Coolio. I should pay a visit to druntime.. sometime. :)

On 12/12/10, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 On Saturday 11 December 2010 19:00:55 Jonathan M Davis wrote:
 On Saturday 11 December 2010 18:18:54 Tomek Sowi=F1ski wrote:
 string s =3D "";
 assert(s);  // ok
 assert(s !=3D null);  // fails

 I guess that's a bug. But which one is right?

A null array and an empty array are essentially the same thing as far as=


 is concerned. I believe that the only difference is whether "is null"
 returns true or not. So, this program

 import std.range;
 import std.stdio;

 void main()
 {
     string s =3D "";

     writefln("[%s]", s);
     writeln(s is null);
     writeln(s =3D=3D null);
     writeln(s.empty);
     writeln();

     string t =3D null;

     writefln("[%s]", t);
     writeln(t is null);
     writeln(t =3D=3D null);
     writeln(t.empty);
 }


 prints

 []
 false
 true
 true

 []
 true
 true
 true


 "=3D=3D" is _not_  the correct operator to use to check for null anyway.=


 is the correct operator for that.

 This behavior is intended. Arrays are actually something like this under
 the hood:

 struct array(T)
 {
     T* arr;
     size_t length;
 }


 By declaring an array to be null, all you're doing is setting arr to nul=


 std.range.empty() will check length and ignore arr. "=3D=3D" will presum=


 only check arr if the length of the two arrays is the same. If they are
 the same, then each element is checked fore equality. But if their lengt=


 is zero, then you don't need to check any elements. By doing something
 like

 assert("" =3D=3D null)

 you're likely creating an array which has whose arr value is non-null (b=


 it's not actually part of the array; the memory there is extra capacity
 which will be used if/when you append to the array; it avoids extra memo=


 re-allocations that way) and an array whose arr value is null. Both will
 have their lengths be 0.

 When "is null" is used, it checks that arr is null. If "=3D=3D" is used,=


 checks whether the elements of the two arrays are the same. Since they
 have the same length, that would mean iterating through all of their
 elements to verify that each element is the same. But since there are no
 elements in either, no elements get checked. The fact that the "" array
 has extra capacity is irrelevant. It's not part of the array, just arr.
 So, the fact that

 assert("" !=3D null);

 fails is intended. It's a result of how arrays are designed.

On reflection, I should have used the name ptr instead of array, since th=

 _is_
 its actual name. It slipped my mind I guess...

 - Jonathan M Davis

Dec 11 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 11 December 2010 19:16:29 Jonathan M Davis wrote:
 On Saturday 11 December 2010 19:08:33 Simen kjaeraas wrote:
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 This behavior is intended. Arrays are actually something like this
 under the hood:
 
 struct array(T)
 {
 
     T* arr;
     size_t length;
 
 }

Actually, that is: struct array(T) { size_t length; T* ptr; } To get the layout and names right. ( http://digitalmars.com/d/2.0/abi.html )

Good point. Though actually, I don't think that that's quite right either, because it has capacity as well. It seems like there's an error in the documentation.

On further inspection, it looks like capacity is not actually part of the array struct. It's an external property function which uses garbage collector voodoo to determine the array's capacity. And it looks like the actual struct looks like this (in rt.lifetime in druntime): struct Array2 { size_t length; void* ptr; } So, it's not even templatized. - Jonathan M Davis
Dec 11 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Sun, 12 Dec 2010 03:47:02 +0100
Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:

 The first one should fail since the string has length 0. If you use an
 .idup you would get the correct results:
=20
 void main()
 {
 string s =3D "".idup;
 assert(s);  // fails
 assert(s !=3D null);  // ok
 }
=20
 So I guess it's a bug.
=20
 On 12/12/10, Tomek Sowi=C5=84ski <just ask.me> wrote:
 string s =3D "";
 assert(s);  // ok
 assert(s !=3D null);  // fails

 I guess that's a bug. But which one is right?

 --
 Tomek


Is an empty array in general supposed to be false? (I thought that was wron= g in D.) Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 12 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Sun, 12 Dec 2010 04:00:36 +0100
"Simen kjaeraas" <simen.kjaras gmail.com> wrote:

 So likely, idup on an empty string returns an array with null ptr and
 0 length, while "" is 'allocated' in the data segment, and thus given a
 ptr value.

.dup & .idup should not change a string's truth value. For sure, _this_ is = a bug. I think explicite empty strings/arrays should not have null pointers, as op= posed to uninitialised strings/arrays. Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 12 2010
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
spir <denis.spir gmail.com> wrote:

 On Sun, 12 Dec 2010 04:00:36 +0100
 "Simen kjaeraas" <simen.kjaras gmail.com> wrote:

 So likely, idup on an empty string returns an array with null ptr and
 0 length, while "" is 'allocated' in the data segment, and thus given a
 ptr value.

.dup & .idup should not change a string's truth value. For sure, _this_ is a bug. I think explicite empty strings/arrays should not have null pointers, as opposed to uninitialised strings/arrays.

I'm not sure I agree here. It seems foolish to me to allocate 16 bytes (the minimum allocation size for the GC) for an empty array. Of course, "" is not really an empty array. Rather, it is the equivalent of "\n"[0..$-1]. That said, I think cast(bool)array should be true only in the cases where both ptr and length != 0; -- Simen
Dec 12 2010