www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - ~= call copy ctor?

reply "Namespace" <rswhite4 googlemail.com> writes:
I have a 2 questions.

I have this code:

[code]
import std.stdio;

struct Test {
public:
	this(int i = 0) {
		writeln("Test CTor.");
	}

	this(this) {
		writeln("Test Copy CTor");
	}

	~this() {
		writeln("Test DTor");
	}
}

Test[] _arr;

void copy_save(Test t) {
	_arr ~= t;
}

void by_ref_save(ref Test t) {
	_arr ~= t;
}

[/code]

Why get i with

[code]
void main() {
	Test t = 42;

	by_ref_save(t);
}
[/code]

this output:

Test CTor.
Test Copy CTor
Test DTor

?

Why t is copied?

And the same if i have this:

[code]
void main() {
	Test t = 42;

	copy_save(t);
}
[/code]

t is already a clone. Why it is copied again?

Thanks in advance. :)
Jul 19 2012
next sibling parent reply Matthias Walter <xammy xammy.info> writes:
On 07/19/2012 02:27 PM, Namespace wrote:
 I have a 2 questions.
 
 I have this code:
 
 [code]
 import std.stdio;
 
 struct Test {
 public:
     this(int i = 0) {
         writeln("Test CTor.");
     }
 
     this(this) {
         writeln("Test Copy CTor");
     }
 
     ~this() {
         writeln("Test DTor");
     }
 }
 
 Test[] _arr;
 
 void copy_save(Test t) {
     _arr ~= t;
 }
 
 void by_ref_save(ref Test t) {
     _arr ~= t;
 }
 
 [/code]
 
 Why get i with
 
 [code]
 void main() {
     Test t = 42;
 
     by_ref_save(t);
 }
 [/code]
 
 this output:
 
 Test CTor.
 Test Copy CTor
 Test DTor
As you mentioned in the subject the copy constructor is not called while the struct is passed by reference but on array concatenation. But this must occur since the array has its memory region and the original variable t in main() also has. Since they are different, at some point the struct must be copied from t in main() into the array. The "ref" only ensures that it is passed via reference (a pointer to the memory of t in main()) to the function by_ref_save().
 And the same if i have this:
 
 [code]
 void main() {
     Test t = 42;
 
     copy_save(t);
 }
 [/code]
 
 t is already a clone. Why it is copied again?
Here it is once copied from t in main() into the local variable t in copy_save() and then copied (as in your 1st example) into the array. Imagine the copy_save routine is in a different module and only its signature is exposed to main. When calling the function, the compiler does not know what happens inside copy_save but it must put the variable onto the stack at the position of the first parameter (well, in this case it is probably put into a CPU register, but that doesn't matter here). Then, when copy_save is invoked it only knows the local variable which it then copies into the array memory (after enlarging the array). It *may* be the when you enable compiler optimizations (-O -inline) that copy_save gets inlined and hence the compiler can optimize one of the copy calls away. But that I don't know for sure. Best regards, Matthias
Jul 19 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
Is there any way to avoid the implizit copy ctor by array 
concatenation?
Or is the only way to use a pointer?
Jul 19 2012
parent reply Matthias Walter <xammy xammy.info> writes:
On 07/19/2012 03:00 PM, Namespace wrote:
 Is there any way to avoid the implizit copy ctor by array concatenation?
 Or is the only way to use a pointer?
Yes, in some way you have to. If you want to not copy a lot of data (or avoid additional on-copy effort) you either have to you pointers explicitly (Test*[]) or implicitly. The latter works e.g. by making Test a class which means that the variable actually does store a pointer to the instance. Note that for struct pointers you should not escape them, i.e. have a variable local in a function, puts its address into some array and returning that array. Since the local variable is gone, the pointer is invalid as well. (Nothing happens if you do not access it). Best regards, Matthias
Jul 19 2012
parent "Namespace" <rswhite4 googlemail.com> writes:
Ok, so if a put a struct into an array, it will copied into the 
array. But then? How it is deleted?

For exmaple, i have this code:

[code]
import std.stdio;

struct Test {
public:
	static uint _counter;

	this(int i = 0) {
		writeln("Test CTor.");

		_counter++;
	}

	this(this) {
		writeln("Test Copy CTor");

		_counter++;
	}

	~this() {
		writeln("Test DTor");

		if (_counter > 0) {
			_counter--;
		}
	}
}

void main() {
	{
		Test[] _arr;

		void copy_save(Test t) {
			_arr ~= t;
		}

		void by_ref_save(ref Test t) {
			_arr ~= t;
		}

		Test t = 42;

		//copy_save(t);
		by_ref_save(t);

		writeln("end scope");
	}

	writefln("Counter: %d", Test._counter);
}
[/code]

_counter is still 1 but the scope is released. How is that 
possible?
Even with _arr.clear(); at the end of the scope, _counter is 
still 1.
I see one CTor and one Copy CTor but only one DTor.
Jul 19 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
Use std.algorithm.move if you want to avoid the copy ctor call.
Jul 19 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Thursday, 19 July 2012 at 14:31:02 UTC, Timon Gehr wrote:
 Use std.algorithm.move if you want to avoid the copy ctor call.
With "move" I see the lost DTor call, but not without. Ist that a bug? o.O
Jul 19 2012
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 19 July 2012 at 15:36:01 UTC, Namespace wrote:
 _counter is still 1 but the scope is released. How is that 
 possible?
 Even with _arr.clear(); at the end of the scope, _counter is 
 still 1.
 I see one CTor and one Copy CTor but only one DTor.
_arr is actually a dynamic array, which allocates from the garbage collector. When _arr goes out of scope, you are only destroying the "Slice" that is looking at the data, but not the data itself. You have to wait until the Garbage Collector runs and collects. Either that, or use a stack allocated static array, but I wouldn't recommend it. Furthermore, D does not actually guarantee that all your objects are destroyed (eg. destructors called) when the program ends. D's approach is "The program has ended, ergo anything left in memory is moot. Just let the OS clear it." If you really need RAII, you can use RefCounted objects, such as RefCounted! directly, or by using Array.
Jul 19 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
New question:

I have this code:
[code]
import std.stdio;

struct Test {
public:
	this(int i = 0) {
		writeln("CTOR");
	}

	this(this) {
		writeln("COPY CTOR");
	}

	~this() {
		writeln("DTOR");
	}
}

void main() {
	Test[] _arr;

	_arr ~= Test(0);

	writeln("end main");
}
[/code]

And as output i see:

CTOR
COPY CTOR
DTOR
end main

Why on earth....?

I create a struct Test. It's not a local variable, it's directly 
assigned,
but it is copied and the original is destroyed. Why?
If i store something important, like a pointer, in Test and will 
free him in the DTOR i cannot assign this way Test's to an array, 
because the pointer was deleted because the DTOr was called.

I think the correct output would be:

CTOR
end main

Maybe DTOR before "end main". But not COPY CTOR anywhere.
Jul 20 2012
next sibling parent reply "Regan Heath" <regan netmail.co.nz> writes:
On Fri, 20 Jul 2012 14:50:19 +0100, Namespace <rswhite4 googlemail.com>  
wrote:

 New question:

 I have this code:
 [code]
 import std.stdio;

 struct Test {
 public:
 	this(int i = 0) {
 		writeln("CTOR");
 	}

 	this(this) {
 		writeln("COPY CTOR");
 	}

 	~this() {
 		writeln("DTOR");
 	}
 }

 void main() {
 	Test[] _arr;

 	_arr ~= Test(0);

 	writeln("end main");
 }
 [/code]

 And as output i see:

 CTOR
 COPY CTOR
 DTOR
 end main

 Why on earth....?

 I create a struct Test. It's not a local variable, it's directly  
 assigned,
 but it is copied and the original is destroyed. Why?
Because that's how assignment works, rhs is evaluated, struct is constructed, then assigned to lhs, lhs copies the variable into the array. Your variable is a struct, so it is copied. Use a pointer instead and the pointer is copied into the array. Use a reference and the reference is copied into the array. The variable, whatever it is, is /always/ copied into the array. It's a long standing optimisation issue and the reason for the recent distinction between lvalues and rvalues made in C++0x for their new 'move' optimisation where assignments involving rvalues/temporaries can move the rvalue instead of copying - or something like that, I'm a bit vague having watched a vid a few days back and not paying a lot of attention to it, sorry. Does: _arr[0] = Test(0); avoid the copy construction? R
Jul 20 2012
parent "Namespace" <rswhite4 googlemail.com> writes:
 Does:

 _arr[0] = Test(0);

 avoid the copy construction?

 R
[code] void main() { Test[2] _arr = void; _arr[0] = Test(0); writeln("end main"); } [/code] puts CTOR DTOR end main DTOR DTOR If i wrote Test[] instead of Test[2] i get an exception. There is no dynamic way for that problem? Even with "move" it prints "COPY CTOR"...
Jul 20 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
For correctness:
A copy call is still fine if I assign a local variable to _arr 
(even if i think that ref parameters should'nt be copied), but i 
think that it is not fine if I assign directly, as you see above.
Jul 20 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, July 20, 2012 15:50:19 Namespace wrote:
 Why on earth....?
http://stackoverflow.com/questions/6884996/questions-about-postblit-and-move- semantics If you want to guarantee that you don't get any copies while moving an object around, it needs to be a reference type (which would include a pointer to a value type). - Jonathan M Davis
Jul 20 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 20 July 2012 at 13:50:20 UTC, Namespace wrote:
 New question:

 I have this code:
 [code]
 import std.stdio;

 struct Test {
 public:
 	this(int i = 0) {
 		writeln("CTOR");
 	}
Be careful about "int i = O". Structs are not allowed to have default constructors. This is so they can have a static default value of Test.init which can be efficiently mem-copied.
 	this(this) {
 		writeln("COPY CTOR");
 	}

 	~this() {
 		writeln("DTOR");
 	}
 }

 void main() {
 	Test[] _arr;

 	_arr ~= Test(0);

 	writeln("end main");
 }
 [/code]

 And as output i see:

 CTOR
 COPY CTOR
 DTOR
 end main

 Why on earth....?

 I create a struct Test. It's not a local variable, it's 
 directly assigned, but it is copied and the original is 
 destroyed. Why?
Seems perfectly reasonable to me. ~= is an operator, eg, a function that needs arguments. Test(0) _IS_ a local variable that you pass as an argument in main (CTOR), and you pass it to the operator. From there, operator!"~=" will build a new object straight from the old object (COPY CTOR). Then, you return to main, and the stack local Test(T) object is destroyed (DTOR). Finally, main ends (end main). The last missing destructor is the one of the object in the array.
 If i store something important, like a pointer, in Test and 
 will free him in the DTOR i cannot assign this way Test's to an 
 array, because the pointer was deleted because the DTOr was 
 called.
Well if you do that, then your object can't be copied ever. -------- Here is a funner test though: Though: Test TestBuilder(int i) { return Test(i); } void main() { Test[] _arr; _arr ~= TestBuilder(3); writeln("end main"); } Output: CTOR COPY CTOR end main _This_ smells funky to me. There should be a destroyer somewhere in there: At the end of the day, there is only 1 element left in memory, but two have been created, yet none have been destroyed !?
Jul 20 2012
prev sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, July 20, 2012 08:08:47 Jonathan M Davis wrote:
 On Friday, July 20, 2012 15:50:19 Namespace wrote:
 Why on earth....?
http://stackoverflow.com/questions/6884996/questions-about-postblit-and-move - semantics If you want to guarantee that you don't get any copies while moving an object around, it needs to be a reference type (which would include a pointer to a value type).
Alternatively, you can disable this(this), which would make copies illegal, but then I'd be worried about code breaking when the compiler adjusted some of its optimizations (though it would generally be unlikely for a move to become a copy - the opposite would be far more likely, which wouldn't break anything). - Jonathan M Davs
Jul 20 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
If i  disable the postblit i get a strange behaviour:

[code]
struct Test {
public:
	int _id;

	this(int i) {
		_id = i;

		writeln("CTOR");
	}

	 disable
	this(this);

	~this() {
		writeln("DTOR");
	}
}

void main() {
	Test[] _arr;

	_arr ~= Test(42);

	writeln(_arr[0]._id);

	writeln("end main");
}
[/code]

prints:

CTOR
DTOR
42
end main

The DTor is called _before_ i write Test's id but i can still 
print the correct id.
Implicit Copy CTor? ;)
Jul 20 2012
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 20 July 2012 at 16:02:18 UTC, Namespace wrote:
 If i  disable the postblit i get a strange behaviour:

 	 disable
 	this(this);
I think disable is broken right now, and does nothing (ei: allows a default implementation of this(this) ). If you replace by the C++ "declare but don't implement" scheme: "this(this);" Then you get a normal linker error. So to answer your question: Yes, implicit Copy CTor.
Jul 20 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
It seems structs are very broken right now...
I would use classes, but they are nullable and without 
correct/good error handling instead of "access violation" i 
prefer structs.
Funny, all variables are default initialized which would be even 
done by the OS. But Null Exceptions aren't exists and D let the 
OS thrown an uninformative message (at least on windows).

I think the only "good" decision is to store important thinks 
like pointers eternal and let the GC free them. Or use an 
reference counter.
Jul 20 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
Something else which is against classes: incorrect scope
behaviour:

[code]
import std.stdio;

class TestC {
public:
	this() {
		writeln("CTOR class");
	}

	~this() {
		writeln("DTOR class");
	}
}

struct TestS {
public:
	this(int i) {
		writeln("CTOR struct");
	}

	~this() {
		writeln("DTOR struct");
	}
}

void main() {
	{
		writeln("begin scope");

		TestC c  = new TestC();
		TestS s = TestS(42);

		writeln("end scope");
	}

	writeln("end main");
}
[/code]

Prints

begin scope
CTOR class
CTOR struct
end scope
DTOR struct
end main
DTOR class

Why comes "DTOR class" _after_ "end main" and not before?
If i write "scope TestC c = ...;" it is correct, but i read that
"scope" will be deprecated. Can someone explain me that behaviour?
Jul 20 2012
next sibling parent reply "David Nadlinger" <see klickverbot.at> writes:
On Friday, 20 July 2012 at 21:41:45 UTC, Namespace wrote:
 Why comes "DTOR class" _after_ "end main" and not before?
 If i write "scope TestC c = ...;" it is correct, but i read that
 "scope" will be deprecated. Can someone explain me that 
 behaviour?
The destructor will be invoked when the GC collects the object, not when the last reference to it goes »out of scope«. David
Jul 20 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Friday, 20 July 2012 at 21:51:02 UTC, David Nadlinger wrote:
 On Friday, 20 July 2012 at 21:41:45 UTC, Namespace wrote:
 Why comes "DTOR class" _after_ "end main" and not before?
 If i write "scope TestC c = ...;" it is correct, but i read 
 that
 "scope" will be deprecated. Can someone explain me that 
 behaviour?
The destructor will be invoked when the GC collects the object, not when the last reference to it goes »out of scope«. David
Really? Holy crap... Can i correct this? And will "scope" for classes really deprecated?
Jul 20 2012
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/20/2012 02:54 PM, Namespace wrote:
 On Friday, 20 July 2012 at 21:51:02 UTC, David Nadlinger wrote:
 On Friday, 20 July 2012 at 21:41:45 UTC, Namespace wrote:
 Why comes "DTOR class" _after_ "end main" and not before?
 If i write "scope TestC c = ...;" it is correct, but i read that
 "scope" will be deprecated. Can someone explain me that behaviour?
The destructor will be invoked when the GC collects the object, not when the last reference to it goes »out of scope«. David
Really? Holy crap... Can i correct this? And will "scope" for classes really deprecated?
The truth is, the destructor may not ever be called. Yes scope is (will be?) deprecated and is replaced by std.typecons.scoped: import std.typecons; // ... auto c = scoped!TestC(); Prints: begin scope CTOR class CTOR struct end scope DTOR struct DTOR class end main Ali
Jul 20 2012
prev sibling parent "Lukasz" <wrzoski gmail.com> writes:
On Friday, 20 July 2012 at 21:54:05 UTC, Namespace wrote:
 On Friday, 20 July 2012 at 21:51:02 UTC, David Nadlinger wrote:
 On Friday, 20 July 2012 at 21:41:45 UTC, Namespace wrote:
 Why comes "DTOR class" _after_ "end main" and not before?
 If i write "scope TestC c = ...;" it is correct, but i read 
 that
 "scope" will be deprecated. Can someone explain me that 
 behaviour?
The destructor will be invoked when the GC collects the object, not when the last reference to it goes »out of scope«. David
Really? Holy crap... Can i correct this? And will "scope" for classes really deprecated?
I suppose that you need this: http://dlang.org/phobos/std_typecons.html#scoped
Jul 20 2012
prev sibling parent reply "Lukasz" <wrzoski gmail.com> writes:
On Friday, 20 July 2012 at 21:41:45 UTC, Namespace wrote:
 Something else which is against classes: incorrect scope
 behaviour:

 [code]
 import std.stdio;

 class TestC {
 public:
 	this() {
 		writeln("CTOR class");
 	}

 	~this() {
 		writeln("DTOR class");
 	}
 }

 struct TestS {
 public:
 	this(int i) {
 		writeln("CTOR struct");
 	}

 	~this() {
 		writeln("DTOR struct");
 	}
 }

 void main() {
 	{
 		writeln("begin scope");

 		TestC c  = new TestC();
 		TestS s = TestS(42);

 		writeln("end scope");
 	}

 	writeln("end main");
 }
 [/code]

 Prints

 begin scope
 CTOR class
 CTOR struct
 end scope
 DTOR struct
 end main
 DTOR class

 Why comes "DTOR class" _after_ "end main" and not before?
 If i write "scope TestC c = ...;" it is correct, but i read that
 "scope" will be deprecated. Can someone explain me that 
 behaviour?
That happens because the destructor is being called when Garbage collector is cleaning the memory used by the class instance.
Jul 20 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
 That happens because the destructor is being called when Garbage
 collector is cleaning the memory used by the class instance.
How can i call the DTOR or at least a Release method after end of scope? Optimally automatic without any explicit calls.
Jul 20 2012
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, July 21, 2012 00:04:21 Namespace wrote:
 That happens because the destructor is being called when Garbage
 collector is cleaning the memory used by the class instance.
How can i call the DTOR or at least a Release method after end of scope? Optimally automatic without any explicit calls.
You can use std.typecons.scoped, but then you can't pass the class around, which is exactly what would have happened with scoped. It's inherently unsafe. You can do something like MyObj obj = createObj(); scope(exit) clear(obj); but again, it's going to be destroyed immediately when it leaves scope, and any reference to it which escapes will blow up when you use it. In neither case is there any reference counting. You could use std.typecons.RefCounted, but unless _every_ instance of the object is wrapped in it (unfortunately, it uses alias this on its payload, making it easy to accidentally have references to its payload escape - which will be screwed up when the ref count reaches zero, and the payload is destroyed) The reality of the matter is that if you want deterministic destruction, you should be using a struct, not a class. There are ways to force a class to be destroyed when it leaves scope, but they're inherently unsafe, so they should only be used if you really need them and know what you're doing. Classes are intended to live on the GC heap and be destroyed and freed by the GC when a garbage collection cycle finds that nothing refers to that object anymore. - Jonathan M Davis
Jul 20 2012
prev sibling next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/20/2012 09:02 AM, Namespace wrote:
 If i  disable the postblit i get a strange behaviour:
Something is not right. Compiled with dmd 2.059, the linker says: undefined reference to `_D6deneme4Test10__postblitMFZv' 64 bit Linux... Ali
Jul 20 2012
prev sibling parent reply "Kenji Hara" <k.hara.pg gmail.com> writes:
On Friday, 20 July 2012 at 16:02:18 UTC, Namespace wrote:
 If i  disable the postblit i get a strange behaviour:

 [code]
 struct Test {
 public:
 	int _id;

 	this(int i) {
 		_id = i;

 		writeln("CTOR");
 	}

 	 disable
 	this(this);

 	~this() {
 		writeln("DTOR");
 	}
 }

 void main() {
 	Test[] _arr;

 	_arr ~= Test(42);

 	writeln(_arr[0]._id);

 	writeln("end main");
 }
 [/code]

 prints:

 CTOR
 DTOR
 42
 end main

 The DTor is called _before_ i write Test's id but i can still 
 print the correct id.
 Implicit Copy CTor? ;)
Array appending and struct postblit behavior is now broken. I've suggested a pull request to fix these bugs. https://github.com/D-Programming-Language/dmd/pull/1037 Bye. Kenji Hara
Jul 21 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
That's great. Hope 2.060 fixes many errors, especially in structs.
BTW: When comes 20060? I thought dmd versions come in cycles of 
2-3 months?
Jul 21 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, July 21, 2012 22:18:40 Namespace wrote:
 That's great. Hope 2.060 fixes many errors, especially in structs.
 BTW: When comes 20060? I thought dmd versions come in cycles of
 2-3 months?
Usually, they do, but Walter got caught up in working on COFF support for Windows and there's no sign of him looking to do a release before that's done. So, it could actually be a few months away. It probably would have been better to do a release _before_ adding all of the COFF support, but that doesn't seem to be happening. That's partially why that whole discussion on creating a D stable branch popped up in the main newsgroup, and there's now a side project created to do that as announced in the announce group (though, they intended to start with 2.060, so I don't know what they're going to do given the delay). - Jonathan M Davis
Jul 21 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
Ok. I hope it will not take months. :/

BTW:
I have a new strange behaviour by experiementing with structs.

This code http://dpaste.dzfl.pl/new#top
works fine, as long as i don't do _any_ operation after line 91.
If i activate one of the out comment operations or something 
else, the program works still fine but crashes after termination.

I use Windows 7, dmd 2.059 and compile with
-w -O -property -unittest -debug

I have no explanation.
Jul 21 2012
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
I have tried various compiler flag combinations, even without.
But it still crashes. Seems to be a Windows bug.
Jul 21 2012
parent "Namespace" <rswhite4 googlemail.com> writes:
On Saturday, 21 July 2012 at 20:48:16 UTC, Namespace wrote:
 I have tried various compiler flag combinations, even without.
 But it still crashes. Seems to be a Windows bug.
I'm stupid... I only compiles but not save my paste. Here is the Code: http://dpaste.dzfl.pl/d385c56b
Jul 21 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, July 21, 2012 22:41:10 Namespace wrote:
 Ok. I hope it will not take months. :/
Well, it's looking like it's going to take at least a month or two, but we'll see. It'll likely depend on how quickly Walter can complete what he's working on. He could have it done by the end of the month for all I know, but adding COFF support will take some time. If you absolutely need the fixes that are currently on github sooner, you can always build the version on github. The easiest way to do that would probably be using DVM: https://bitbucket.org/doob/dvm/wiki/Home
 BTW:
 I have a new strange behaviour by experiementing with structs.
 
 This code http://dpaste.dzfl.pl/new#top
 works fine, as long as i don't do _any_ operation after line 91.
 If i activate one of the out comment operations or something
 else, the program works still fine but crashes after termination.
 
 I use Windows 7, dmd 2.059 and compile with
 -w -O -property -unittest -debug
 
 I have no explanation.
If you find a bug, please report it: http://d.puremagic.com/issues - Jonathan M Davis
Jul 21 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
I'm not sure if it's a bug or my code is nonsense. ;)
Jul 21 2012
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Saturday, 21 July 2012 at 21:12:55 UTC, Namespace wrote:
 I'm not sure if it's a bug or my code is nonsense. ;)
While there are some bugs D, you have to realize that D is a garbage collected language, which means you should not rely on objects being destructed in a timely fashion (or at all). Furthermore, it discourages manipulating raw pointers. While experimenting is fine and all (and I encourage it), you really need to embrace the language for what it is, rather than force a design on to it (scoped!). I'd be willing to bet big money you are coming from C++? I had the same difficulties at first.
Jul 22 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
Yes, C++ was my previous language.

And no, nobody say anything about Ranges. But i doesn't like 
stuff who is deprecated as language feature and is rebuild as 
std.algorithm construct. It is that simple. :)
Jul 22 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, July 22, 2012 10:16:25 Namespace wrote:
 Yes, C++ was my previous language.
 
 And no, nobody say anything about Ranges. But i doesn't like
 stuff who is deprecated as language feature and is rebuild as
 std.algorithm construct. It is that simple. :)
std.algorithm doesn't have anything like that. Are you talking about std.typecons.scoped? Regardless, it was decided that it was _bad_ language feature, which is why it was removed from the language (or will be anyway - I don't think that it's actually been deprecated yet like it's supposed to be). The same goes for delete. They're _not_ safe when dealing with GC memory, and their use should generally be strongly discouraged. - Jonathan M Davis
Jul 22 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
Oh yes, i mean std.typecons, not std.algorithm, my bad.

If delete will be deprecated, how can i delete/call dtor of one 
of my objects manually?
Jul 22 2012
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, July 22, 2012 10:46:51 Namespace wrote:
 Oh yes, i mean std.typecons, not std.algorithm, my bad.
 
 If delete will be deprecated, how can i delete/call dtor of one
 of my objects manually?
You're _really_ not supposed to be doing that. If you're dealing with GC allocated memory, then it's the GC that should be freeing it, not you. It's unsafe for you to be doing it, and it's just asking for bugs. If you want to be manually managing memory, you should be using malloc and free (which admittedly is a bit of pain with objects right now - custom allocators should fix that once we get them). But if you absolutely insist on trying to manage GC memory manually, then use the stuff in core.memory: http://dlang.org/phobos/core_memory.html But again, that's really only for when you _really_ need it. If you're doing much with it, then it's just about a guarantee that you're not using D as it's intended to be used. You're trying to program in C++ with D, which is just going to cause you trouble. - Jonathan M Davis
Jul 22 2012
parent "Namespace" <rswhite4 googlemail.com> writes:
The reason for my experiements is just to find a good solution 
for Not Null types. :)
As long as they are not implement in std.typecons. Maybe you take 
my solution, if i have some. ;)
Jul 22 2012
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/22/2012 01:46 AM, Namespace wrote:
 Oh yes, i mean std.typecons, not std.algorithm, my bad.

 If delete will be deprecated, how can i delete/call dtor of one of my
 objects manually?
In C++, deleting involves two steps: call the destructor and release the memory. It is different in D: releasing memory should be the responsibility of the GC; after all, GC is the one that runs some algorithm that fits its needs. On the other hand, calling the destructor is still acceptable in D because it may be important for the programmer to run the contents earlier than GC would. clear() does that: auto t = new Test(f3); // ... clear(t); // <-- Run the destructor Unfortunately it has a bad name, which is going to be changed. Ali
Jul 22 2012
next sibling parent reply "Mafi" <mafi example.org> writes:
 On the other hand, calling the destructor is still acceptable 
 in D because it may be important for the programmer to run the 
 contents earlier than GC would. clear() does that:

     auto t = new Test(f3);
     // ...
     clear(t);    // <-- Run the destructor

 Unfortunately it has a bad name, which is going to be changed.

 Ali
Really? I thought we have to stay with this name now. In my opinion this name is quite bad, especially in the presence of UFCS. What is going to be changed to? destroy()? Mafi
Jul 22 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/22/2012 09:27 AM, Mafi wrote:

 Unfortunately it has a bad name, which is going to be changed.
 Really? I thought we have to stay with this name now. In my
 opinion this name is quite bad, especially in the presence of
 UFCS. What is going to be changed to? destroy()?
Yep! :) It should be available in 2.060: // Scheduled for deprecation in December 2012. // Please use destroy instead of clear. https://github.com/D-Programming-Language/druntime/blob/master/src/object.di#L682 Ali
Jul 22 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 22 July 2012 at 17:41:33 UTC, Ali Çehreli wrote:
 On 07/22/2012 09:27 AM, Mafi wrote:

 Unfortunately it has a bad name, which is going to be
changed.
 Really? I thought we have to stay with this name now. In my
 opinion this name is quite bad, especially in the presence of
 UFCS. What is going to be changed to? destroy()?
Yep! :) It should be available in 2.060: // Scheduled for deprecation in December 2012. // Please use destroy instead of clear. https://github.com/D-Programming-Language/druntime/blob/master/src/object.di#L682 Ali
And how do I check if my object is still valid?
Jul 22 2012
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, July 22, 2012 20:14:20 Namespace wrote:
 On Sunday, 22 July 2012 at 17:41:33 UTC, Ali =C3=87ehreli wrote:
 On 07/22/2012 09:27 AM, Mafi wrote:
 Unfortunately it has a bad name, which is going to be
=20 changed. =20
 Really? I thought we have to stay with this name now. In my
 opinion this name is quite bad, especially in the presence of
 UFCS. What is going to be changed to? destroy()?
=20 Yep! :) It should be available in 2.060: =20 // Scheduled for deprecation in December 2012. // Please use destroy instead of clear. =20 https://github.com/D-Programming-Language/druntime/blob/master/src/=
object.
 di#L682
=20
 Ali
=20 And how do I check if my object is still valid?
You don't. The object is completely invalid at that point. Its vtbl has= been=20 zeroed out. Asking how you check whether it's valid is like asking how = you can=20 tell whether an object in C++ which has been deleted is valid. It just = doesn't=20 work that way. If you're destroying it, you need to then _never_ use it= for=20 _anything_. You can set a reference which pointed to it to null, and th= en it's=20 null, and you can check for null, but an object which has had clear/des= troy=20 called on it is in an invalid state and should not be touched. It's=20 specifically set up such that it's highly likely to cause your program = to crash=20 if you try and use it at all. If you want to check an object for validi= ty, you=20 need to do something else. - Jonathan M Davis
Jul 22 2012
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 22 July 2012 at 18:14:21 UTC, Namespace wrote:
 On Sunday, 22 July 2012 at 17:41:33 UTC, Ali Çehreli wrote:
 On 07/22/2012 09:27 AM, Mafi wrote:

 Unfortunately it has a bad name, which is going to be
changed.
 Really? I thought we have to stay with this name now. In my
 opinion this name is quite bad, especially in the presence of
 UFCS. What is going to be changed to? destroy()?
Yep! :) It should be available in 2.060: // Scheduled for deprecation in December 2012. // Please use destroy instead of clear. https://github.com/D-Programming-Language/druntime/blob/master/src/object.di#L682 Ali
And how do I check if my object is still valid?
AFAIK, there are no invalid objects in D*. structs don't have default constructors, because they all have a compile time "init" value which they are filled with when "emptied"/"moved"/"cleared". Ergo, even after being "destroyed", they are still valid. Arrays are set to a null slice. Pointers and class references are set to null. To check a class reference, then "if(instance is null)" should do what you want BTW. *Provided you don't go out of your way to give them an invalid state, of course.
Jul 22 2012
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, July 22, 2012 20:45:05 monarch_dodra wrote:
 AFAIK, there are no invalid objects in D*. structs don't have
 default constructors, because they all have a compile time "init"
 value which they are filled with when
 "emptied"/"moved"/"cleared". Ergo, even after being "destroyed",
 they are still valid.
 
 Arrays are set to a null slice.
 
 Pointers and class references are set to null.
 
 To check a class reference, then "if(instance is null)" should do
 what you want BTW.
 
 *Provided you don't go out of your way to give them an invalid
 state, of course.
Nothing in D is in an invalid state as long as you place nice, but it's perfectly possible to put an object in an invalid state if you try. int i = void; will make i garbage. It avoids the initialization and is therefore an optimization technique, but by using it, you do get a variable which is an an invalid state. Calling clear on a class object puts it in an invalid state. It calls its finalizer and zeroes out its vtbl. It's _not_ null, and using it will likely result in a segfault (some non-virtual stuff may still work, but anything virtual will cause a segfault because of the zeroed out vtbl). Calling clear on pretty much everything else is safe, since it generally just sets everything else to null without doing any kind of destruction or cleanup. The on exception would be structs, and with them, they get put in their init state if they're on the stack, and if they're on the heap, the pointer to them gets set to null. The struct itself is untouched. But for classes, it's _not_ safe. The object _does_ get put in an invalid state. Stuff like initializing to void or calling clear on an object are really only there so that you have more fine-grained control when you really need it, and they come with definite responsibility when you do use them, because you're telling the language that you don't need its full protection and that you know what you're doing. They really shouldn't be used in your average D program. - Jonathan M Davis
Jul 22 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
Nice to know. A name like "destroy" or "delete " would be better 
i think.
But isn't a solution for the not null problem.
Jul 22 2012
prev sibling next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
A other question:
How can i check if t is valid, after i call "clear"?

[code]
import std.stdio;

class Test {
public:
	this() {
		writeln("CTor");
	}

	~this() {
		writeln("DTor");
	}
}

void main() {
	Test t = new Test();

	clear(t);

	writeln("end main");
}
[/code]

If i write assert(t !is null); it fails, and if i write 
writeln(t); or assert(t); i get an access violation.
Jul 22 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/22/2012 09:35 AM, Namespace wrote:
 A other question:
 How can i check if t is valid, after i call "clear"?
After the destructor is run, the object itself is put into a strange state. I amnot really sure about the actual details but I think the virtual function table is cleared (or perhaps just the destructor's entry?) so that the destructor is not going to be run again by the GC later. Regarding the references to it, like 't' below, clear() cannot know about all of the references to the object. It could null the reference that is passed to it but apparently it doesn't do that maybe because null'ing one reference out of many wouldn't buy much. Of course it is always possible to set it to null explicitly after calling clear(): clear(t); t = null; Because now we know that we shouldn't be using the local reference. Ali
Jul 22 2012
parent "Namespace" <rswhite4 googlemail.com> writes:
Works fine. :)
Now my little test case work as expected:

[code]
import std.stdio;

class Test {
public:
	this() {
		writeln("CTor");
	}

	~this() {
		writeln("DTor");
	}

	void echo() const {
		writeln("Here is Test");
	}
}

struct scoped {
private:
	Test* _t;

public:
	this(ref Test t) {
		this._t = &t;

		writeln(" >> scoped CTor");
	}

	 disable
	this(this);

	ref scoped opAssign(ref Test t) {
		this.Release();

		this._t = &t;

		writeln(" >> scoped Assign");

		return this;
	}

	~this() {
		writeln(" >> scoped DTor");
		this.Release();
	}

	void Release() {
		if (this._t !is null && *this._t !is null) {
			writeln("Clear scoped content");

			clear(*this._t);
			*this._t = null;
			this._t = null;
		}
	}

	 property
	Test* Value() {
		return this._t;
	}

	alias Value this;
}

class Bar {
private:
	scoped _sc;

public:
	this(ref Test f) {
		writeln("begin this");
		this._sc = f;
		writeln("end this");
	}

	~this() {
		writeln("DTOR BAR");
	}
}

void main() {
	Test t = new Test();

	clear(t);

	Test t2 = new Test();

	{
		scoped s = t2;
		s.echo();

		writeln("end scope");
	}

	assert(t2 is null);

	Test t3 = new Test();
	Bar b = new Bar(t3);

	assert(t3 !is null);

	writeln("end main");
}
[/code]
Jul 22 2012
prev sibling parent "Namespace" <rswhite4 googlemail.com> writes:
A other question:
How can i check if t is valid, after i call "clear"?

[code]
import std.stdio;

class Test {
public:
	this() {
		writeln("CTor");
	}

	~this() {
		writeln("DTor");
	}
}

void main() {
	Test t = new Test();

	clear(t);

	writeln("end main");
}
[/code]

If i write assert(t !is null); it fails, and if i write
writeln(t); or assert(t); i get an access violation.
Jul 22 2012
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/21/2012 01:41 PM, Namespace wrote:
 Ok. I hope it will not take months. :/

 BTW:
 I have a new strange behaviour by experiementing with structs.

 This code http://dpaste.dzfl.pl/new#top
 works fine, as long as i don't do _any_ operation after line 91.
 If i activate one of the out comment operations or something else, the
 program works still fine but crashes after termination.

 I use Windows 7, dmd 2.059 and compile with
 -w -O -property -unittest -debug

 I have no explanation.
I was able to reproduce the segmentation fault when I compiled with -m32 under a 64 bit Linux environment. And this much is sufficient to reproduce the problem: void main() { Foo f3 = new Foo(); Test t = new Test(f3); writeln("exit main"); // <-- note the added line } One thing that I've noticed is that opAssign() is probably buggy because it overwrites this._ptr without taking care of it first (i.e. it is not doing '*_ptr = null'). But I don't think it is related to the main issue. 'Test' is a class having a struct member. The destructor of that member is accessing memory that is already gone. The last line in main makes it more clear how that destructor is accessing an already destroyed pointer: this this exit main <-- DTOR TEST DESTROY SCOPED make: *** [deneme] Segmentation fault _sc_f._ptr points to f3, which is a local object in main. Ali
Jul 21 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
Cool. Any idea how to fix it?
Check whether this._ptr! is null && *this._ptr! is null does not 
help.
Although I'm just experimenting, but it would be nice to have a 
solution.
Jul 21 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
First, my earlier writeln("exit main"); wasn't achieving much without 
putting everything before it in a scope (like it is below).

On 07/21/2012 03:12 PM, Namespace wrote:
 Cool. Any idea how to fix it?
 Check whether this._ptr! is null && *this._ptr! is null does not help.
 Although I'm just experimenting, but it would be nice to have a solution.
For Test to own a scoped_ptr member that holds on to a local class reference, it must be destroyed when leaving the same scope as the class reference (not potentially much later when the GC kicks in.) One of guaranteeing it is using std.typecons.scoped: import std.typecons; void main() { { Foo f3 = new Foo(); auto t = scoped!Test(f3); } writeln("exit main"); } Now it is destroyed at the end of its scope (the scope is NOT needed for correctness, just for demonstration): this this DTOR TEST exit main <-- Ali
Jul 21 2012
parent reply "Namespace" <rswhite4 googlemail.com> writes:
The strange constructs in std.algorithm aren't something i like 
to use in real projects. Is there some other solution?
Jul 22 2012
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, July 22, 2012 09:48:39 Namespace wrote:
 The strange constructs in std.algorithm aren't something i like
 to use in real projects. Is there some other solution?
And what's so strange about them? That they use ranges? If you start avoiding ranges, you're going to be missing out on a large portions of the standard library very quickly and will be left on your own to provide that functionality or provide it elsewhere. Phobos is very heavily range based and only becoming more so. If you haven't read this tutorial on ranges yet, you probably should: http://ddili.org/ders/d.en/ranges.html - Jonathan M Davis
Jul 22 2012