View previous topic :: View next topic |
Author |
Message |
krcko
Joined: 31 Aug 2007 Posts: 11 Location: Serbia
|
Posted: Fri Aug 31, 2007 8:41 pm Post subject: Making copy of a class instance? |
|
|
is it possible? and how?
i'm writting stack class (template) to use in my first d project, but i'm having problems with classes. the stack has dup and dupBottom methods for duplicatin top-most and bottom-most items on the stack. it works ok for scalar types (ints, strings...) but classes are reference type as i know, but i must find a way to make a copy!
btw, here's the code for the stack:
Code: |
//
// stack class template
//
class Stack(TYPE)
{
private:
struct stackItem
{
stackItem* above; // points to item which is above this one
stackItem* below; // points to item which is below this one
TYPE data; // holds data
}
stackItem* top = null, bottom = null;
int count = 0;
public:
~this()
{
this.clear;
}
//
// pushes value on top of the stack
//
TYPE push(TYPE value)
{
stackItem* item;
item = new stackItem;
item.data = value;
if (count++)
{
item.below = top;
top.above = item;
top = item;
}
else
{
top = bottom = item;
}
return value;
}
//
// pushes value on bottom of the stack
//
TYPE pushBottom(TYPE value)
{
stackItem* item;
item = new stackItem;
item.data = value;
if (count++)
{
item.above = bottom;
bottom.below = item;
bottom = item;
}
else
{
top = bottom = item;
}
return value;
}
//
// pops value from top of the stack
//
TYPE pop()
{
if (count--)
{
TYPE value = top.data;
stackItem* newTop = top.below;
delete top;
top = newTop;
return value;
}
return cast(TYPE) null;
}
//
// pops value from bottom of the stack
//
TYPE popBottom()
{
if (count--)
{
TYPE value = bottom.data;
stackItem* newBottom = bottom.above;
delete bottom;
bottom = newBottom;
return value;
}
return cast(TYPE) null;
}
//
// peek at value on the top of the stack (returns value without changing the stack)
//
TYPE peek()
{
if (count)
{
return top.data;
}
return cast(TYPE) null;
}
//
// peek at value on the bottom of the stack (returns value without changing the stack)
//
TYPE peekBottom()
{
if (count)
{
return bottom.data;
}
return cast(TYPE) null;
}
//
// duplicates value on top of the stack specified number of times (default is 1 copy)
//
TYPE dup(int copies = 1)
{
if (count)
{
while (copies-- > 0)
{
this.push(top.data);
}
return top.data;
}
return cast(TYPE) null;
}
//
// duplicates value on bottom of the stack specified number of times (default is 1 copy)
//
TYPE dupBottom(int copies = 1)
{
if (count)
{
while (copies-- > 0)
{
this.pushBottom(bottom.data);
}
return bottom.data;
}
return cast(TYPE) null;
}
//
// removes specified number of items from top of the stack
//
TYPE roll(int items)
{
if (count)
{
stackItem* newTop;
while (items-- > 0)
{
if (!count--) break;
newTop = top.below;
delete top;
top = newTop;
}
if (top)
{
return top.data;
}
}
return cast(TYPE) null;
}
//
// removes specified number of items from bottom of the stack
//
TYPE rollBottom(int items)
{
if (count)
{
stackItem* newBottom;
while (items-- > 0)
{
if (!count--) break;
newBottom = bottom.above;
delete bottom;
bottom = newBottom;
}
if (bottom)
{
return bottom.data;
}
}
return cast(TYPE) null;
}
//
// clears the stack
//
void clear()
{
if (count)
{
stackItem* item;
while (bottom != null)
{
item = bottom.above;
delete bottom;
bottom = item;
}
top = null;
count = 0;
}
}
//
// returns true if there is no items on the stack
//
const bool empty()
{
return count == 0;
}
//
// returns number of items on the stack
//
const int items()
{
return count;
}
}
|
and this is a test:
Code: |
class Test
{
int a, b;
}
alias Stack!(Test) StackTest;
import std.stdio;
void main()
{
StackTest s;
Test t1, t2, t3, t4;
t1 = new Test;
t2 = new Test;
s = new StackTest;
t1.a = 34; t1.b = 843;
t2.a = 139; t2.b = 63;
s.push(t1);
s.push(t2);
s.dup;
writefln(t2.a);
t3 = s.pop;
t3.a = 1;
writefln(t2.a);
}
|
which outputs:
meaning that t3 (which was made with .sup and then .pop-ed from the stack) is a reference to t2, and this behavior is no-no!
pls help.
thanks in advance! |
|
Back to top |
|
|
csauls
Joined: 27 Mar 2004 Posts: 278
|
Posted: Sat Sep 01, 2007 12:46 am Post subject: |
|
|
Basically, no. Or at least not with any built-in features. The way to go about it, if you control the source for the class you wish to duplicate, is to have that class expose either a .dup() method of its own (or whatever you choose to call it) and use that, or else a constructor which takes a reference to an existing instance as its parameter. In either case, you're manually duplicating.
Code: |
class Foo {
int a, b;
this (int a, int b) {
this.a = a;
this.b = b;
}
this (Foo foo) {
this(foo.a, foo.b);
}
Foo dup () {
return new Foo(this);
}
}
|
Since your stack is already a template, you could use static if's to determine which is available.
For classes whose source you do not control, one option is to write a sort of factory function.
Code: | Foo dup (Foo foo) {
return new Foo(foo.a, foo.b);
} |
There might be other ways I don't know about. I haven't really needed to do this myself. _________________ Chris Nicholson-Sauls |
|
Back to top |
|
|
krcko
Joined: 31 Aug 2007 Posts: 11 Location: Serbia
|
Posted: Sat Sep 01, 2007 9:13 am Post subject: |
|
|
thanx csauls, but manualy duplicating is my last option, which i'm trying to avoid... i'll see to try some ides which i have... (but they are all in asm, and i'm beginner in asm, so i'll first need to read some manuals and sources...) |
|
Back to top |
|
|
krcko
Joined: 31 Aug 2007 Posts: 11 Location: Serbia
|
Posted: Sat Sep 01, 2007 9:48 pm Post subject: |
|
|
i found a way to make a copy of class, by using windows' CopyMemory and gc's capacity:
Code: |
// on the top:
import std.gc;
extern(Windows)
{
void RtlMoveMemory(void* src, void* dest, int size);
alias RtlMoveMemory CopyMemory;
}
// in template code:
static if (is (TYPE == class))
{
TYPE duplicate = new TYPE;
CopyMemory(cast(void*) duplicate, cast(void*) top.data, capacity(cast(void*) top.data));
this.push(duplicate);
}
else
{
this.push(top.data);
}
|
and here's a test:
Code: |
class Test
{
private:
int foo;
public:
int a, b;
void set(int f)
{
foo = a + b + f;
}
int get()
{
return foo - a + b;
}
}
alias Stack!(Test) StackTest;
import std.stdio;
void main()
{
Test t1, t2, t3;
StackTest s = new StackTest;
t1 = new Test;
t1.a = 123;
t1.b = 456;
t1.set(4);
s.push(t1);
t2 = s.pop;
writefln(t1.a); // 123
t2.a = 789;
writefln(t1.a); // 789
s.push(t1);
s.dup; // make a copy
t3 = s.pop;
writefln(t1.a, " ", t1.get); // 789 250
writefln(t3.a, " ", t3.get); // 789 250
t3.a = 42;
writefln(t1.a); // 789
writefln(t3.a); // 42 YEEPY!
}
|
and it works lika a charm! .dup now really duplicates the value!
but, problems (limitations) with this method are:
- it's not cross-platform
- it can be used only for object created with gc
if anyone knows better solution please tell!!! (atleast, how to get CopyMemory working on linux, i mean, what's the "linux version" of the COpyMemory func?) |
|
Back to top |
|
|
r.lph50
Joined: 27 Nov 2006 Posts: 21 Location: New Zealand
|
Posted: Sun Sep 02, 2007 7:33 am Post subject: |
|
|
If using phobos, memcpy (see the phobos docs) from std.c.string will do the same thing as RtlMoveMemory on windows, linux, etc.
Is your code recursive? i.e. what happens if a class has reference as a member, does the referenced object get copied or just the reference to it? |
|
Back to top |
|
|
Rommie
Joined: 23 Aug 2007 Posts: 6
|
Posted: Sun Sep 02, 2007 1:55 pm Post subject: |
|
|
There is a damn good reason why this cannot be automated in absolutely all cases, and it's exactly the same reason that C++ requires you to write a copy constructor. Quite apart from the problem of member variables which are themselves classes, there is the HUGE problem that the destructor will be called once for each copy. For example, suppose the constructor opens a file, and the destructor closes a file. Your mechanism will open the file once and close it twice. Bang!
D makes it impossible for such bugs to occur. You are trying to find a way to re-introduce them.
I believe the best solution is simply to have your template code call the object's dup() function or copy constructor. If such a function does not exist for a particular specialization, then the code quite rightly should not compile. |
|
Back to top |
|
|
krcko
Joined: 31 Aug 2007 Posts: 11 Location: Serbia
|
Posted: Sun Sep 02, 2007 3:28 pm Post subject: |
|
|
well, it seems that i must do it in that way (to make a dup method) since, i cannot find a way to copy recursive classes (ie classes that have references to other classes as their members)... well i'm going to do manual duplicating after all...
thanks guys |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|