FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Making copy of a class instance?

 
Post new topic   Reply to topic     Forum Index -> General
View previous topic :: View next topic  
Author Message
krcko



Joined: 31 Aug 2007
Posts: 11
Location: Serbia

PostPosted: Fri Aug 31, 2007 8:41 pm    Post subject: Making copy of a class instance? Reply with quote

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:
Code:

139
1

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
View user's profile Send private message MSN Messenger
csauls



Joined: 27 Mar 2004
Posts: 278

PostPosted: Sat Sep 01, 2007 12:46 am    Post subject: Reply with quote

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
View user's profile Send private message AIM Address Yahoo Messenger
krcko



Joined: 31 Aug 2007
Posts: 11
Location: Serbia

PostPosted: Sat Sep 01, 2007 9:13 am    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
krcko



Joined: 31 Aug 2007
Posts: 11
Location: Serbia

PostPosted: Sat Sep 01, 2007 9:48 pm    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
r.lph50



Joined: 27 Nov 2006
Posts: 21
Location: New Zealand

PostPosted: Sun Sep 02, 2007 7:33 am    Post subject: Reply with quote

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
View user's profile Send private message AIM Address
Rommie



Joined: 23 Aug 2007
Posts: 6

PostPosted: Sun Sep 02, 2007 1:55 pm    Post subject: Reply with quote

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
View user's profile Send private message
krcko



Joined: 31 Aug 2007
Posts: 11
Location: Serbia

PostPosted: Sun Sep 02, 2007 3:28 pm    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
Display posts from previous:   
Post new topic   Reply to topic     Forum Index -> General All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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