Polymorphic copy: Difference between revisions

From Rosetta Code
Content added Content deleted
(added php)
(C++)
Line 62: Line 62:
Copied S
Copied S
</pre>
</pre>

=={{header|C plus plus}}==
<cpp>
#include <iostream>

class T
{
public:
virtual void identify() { std::cout << "I am a genuine T" << std::endl; }
virtual T* clone() { return new T(*this); }
virtual ~T() {}
};

class S: public T
{
public:
virtual void identify() { std::cout << "I am an S" << std::endl; }
virtual S* clone() { return new S(*this); }
};

class X // the object which contains a T or S
{
public:
// by getting the object through a pointer to T, X cannot know if it's an S or a T
X(T* t): member(t) {}

// copy constructor
X(X const& other): member(other.member->clone()) {}

// copy assignment operator
X& operator=(X const& other)
{
T* new_member = other.member->clone();
delete member;
member = new_member;
}

// destructor
~X() { delete member; }

// check what sort of object it contains
void identify_member() { member->identify(); }

private:
T* member;
};

int main()
{
X original(new S); // construct an X and give it an S,
X copy = original; // copy it,
copy.identify_member(); // and check what type of member it contains
}
</cpp>


=={{header|Java}}==
=={{header|Java}}==

Revision as of 09:32, 27 October 2008

Task
Polymorphic copy
You are encouraged to solve this task according to the task description, using any language you may know.

An object is polymorphic when its specific type may vary. The types a specific value may take, is called class.

It is trivial to copy an object if its type is known: <c> int x; int y = x; </c> Here x is not polymorphic, so y is declared of same type (int) as x. But if the specific type of x were unknown, then y could not be declared of any specific type.

The task: let a polymorphic object contain an instance of some specific type S derived from a type T. The type T is known. The type S is possibly unknown until run time. The objective is to create an exact copy of such polymorphic object (not to create a reference, nor a pointer to). Let further the type T have a method overridden by S. This method is to be called on the copy to demonstrate that the specific type of the copy is indeed S.

Ada

<ada> with Ada.Text_IO; use Ada.Text_IO;

procedure Test_Polymorphic_Copy is

  package Base is
     type T is tagged null record;
     function Name (X : T) return String;
  end Base;
  use Base;
  
  package body Base is
     function Name (X : T) return String is
     begin
        return "T";
     end Name;
  end Base;
  
     -- The procedure knows nothing about S
  procedure Copier (X : T'Class) is
     Duplicate : T'Class := X;  -- A copy of X
  begin
     Put_Line ("Copied " & Duplicate.Name); -- Check the copy
  end Copier;
  package Derived is   
     type S is new T with null record;
     overriding function Name (X : S) return String;
  end Derived;
  use Derived;
  
  package body Derived is   
     function Name (X : S) return String is
     begin
        return "S";
     end Name;
  end Derived;
  Object_1 : T;
  Object_2 : S;

begin

  Copier (Object_1);
  Copier (Object_2);

end Test_Polymorphic_Copy; </ada> The procedure Copier does not know the specific type of its argument. Nevertheless it creates an object Duplicate of exactly same type. Sample output:

Copied T
Copied S

C plus plus

<cpp>

  1. include <iostream>

class T { public:

 virtual void identify() { std::cout << "I am a genuine T" << std::endl; }
 virtual T* clone() { return new T(*this); }
 virtual ~T() {}

};

class S: public T { public:

 virtual void identify() { std::cout << "I am an S" << std::endl; }
 virtual S* clone() { return new S(*this); }

};

class X // the object which contains a T or S { public:

 // by getting the object through a pointer to T, X cannot know if it's an S or a T
 X(T* t): member(t) {}
 // copy constructor
 X(X const& other): member(other.member->clone()) {}
 // copy assignment operator
 X& operator=(X const& other)
 {
   T* new_member = other.member->clone();
   delete member;
   member = new_member;
 }
 // destructor
 ~X() { delete member; }
 // check what sort of object it contains
 void identify_member() { member->identify(); }

private:

 T* member;

};

int main() {

 X original(new S);      // construct an X and give it an S,
 X copy = original;      // copy it,
 copy.identify_member(); // and check what type of member it contains

} </cpp>

Java

Here we implement a "copy" method once in the base class because there is by default no public way to copy an object from outside the class in Java. (There is a protected, not public, "clone" method inherited from Object.) <java>class T implements Cloneable {

   public String name() { return "T"; }
   public T copy() {
       try {
           return (T)super.clone();
       } catch (CloneNotSupportedException e) {
           return null;
       }
   }

}

class S extends T {

   public String name() { return "S"; }

}

public class PolymorphicCopy {

   public static T copier(T x) { return x.copy(); }
   public static void main(String[] args) {
       T obj1 = new T();
       S obj2 = new S();
       System.out.println(copier(obj1).name()); // prints "T"
       System.out.println(copier(obj2).name()); // prints "S"
   }

}</java>

OCaml

I decided not to use classes and inheritance here because structural subtyping is more natural in OCaml. Oo.copy is polymorphic over all object types. <ocaml>let obj1 =

 object
   method name = "T"
 end

let obj2 =

 object
   method name = "S"
 end

let () =

 print_endline (Oo.copy obj1)#name; (* prints "T" *)
 print_endline (Oo.copy obj2)#name; (* prints "S" *)</ocaml>

PHP

<php><?php class T {

     function name() { return "T"; }

}

class S {

     function name() { return "S"; }

}

$obj1 = new T(); $obj2 = new S(); $obj3 = clone $obj1; $obj4 = clone $obj2; echo $obj3->name(), "\n"; // prints "T" echo $obj4->name(), "\n"; // prints "S" ?></php>

Python

<python>import copy

class T:

   def name(self): return "T"

class S:

   def name(self): return "S"

obj1 = T() obj2 = S() print copy.copy(obj1).name() # prints "T" print copy.copy(obj2).name() # prints "S"</python>

Note the forgoing example uses the Python standard library copy module. However, it gives no insight into what is happening under the hood. Also it takes no consideration of whether a "deep copy" is necessary (using copy.deepcopy). The distinction is important for complex objects containing references to other objects (for instances lists, tuples or dictionaries containing other lists, tuples or dictionaries as elements). The described task, as presented, offers no guidance on this matter.

In many cases the most portable and robust "copy" would be made by serializing the source object and then de-serializing it back into the target. Under Python this would best be done with the pickle or cPickle

<python> import cPickle as pickle

source = {'a': [1, 2.0, 3, 4+6j],

        'b': ('string', u'Unicode string'),
        'c': None}

target = pickle.loads(pickle.dumps(source)) </python>

In this example we use the cPickle module which is an implementation of the pickle features coded in C for optimal performance. We import it as pickle since we intending only on using those features which are common to both implementations. (The pure Python implementation is retained for those who which to create their own classes derived therefrom). The dumps() and loads() methods dump the data structures to a string and load them from a string, respectively. (The more common use of pickle is to serialize the data out to a file or over a network connection).

For the simplest cases one can use simple Python introspection to copy simple objects:

<python> target = source.__class__() # Create an object of the same type if hasattr(source, 'items') and callable(source.items):

   for key,value in source.items:
       target[key] = value

elif hasattr(source, '__len__'):

   target = source[:]

else: # Following is not recommended. (see below).

   target = source

</python>

This example handles dictionaries (and anything that implements a sufficiently dictionary like interface to support the items() method along with the __setitem__() method. (statements of the form x[y] = z in Python are implicitly calling the __setitem__() method of the "x" object, passing it a key of "y" and a value of "z." Similarly this code tests if an item is a sequence (one can call the "len()" built-in function on it) and, if so, uses a slice assignment to perform a shallow copy. For any other type of object a simple binding is performed. Technically this last case will not "copy" anything ... it will create a new name binding to the object to which "source" was already a reference. The earlier binding of a "blank" instance of the source's __class__ will be replaced. So the trick of creating the blank object of the same type is only meaningful for the other types. In the cases of strings, integers and other numbers the objects themselves are immutable and the bindings are all dynamic (so the entire task is moot with respect to them).