Monday, March 4, 2013

OOPS!!! Downcasting

I always wondered what casting is all about when it comes to object oriented programming. I read few related articles and the basic idea I got to start with was:

"With system verilog we need to manually cast the variable from one type to the other type otherwise compiler will issue an error. In System Verilog there are basically two types of casting
1. Static Casting:
A data type can be changed by using a cast ( ' ) operation. In a static cast, the expression to be cast shall be enclosed in parentheses that are prefixed with the casting type and an apostrophe. If the expression is assignment compatible with the casting type, then the cast shall return the value that a variable of the casting type would hold after being assigned the expression.
Example
int’(2.0 * 3.0)
shortint'{{8’hFA,8’hCE}}
2. Dynamic Casting :
SystemVerilog provides the $cast system task to assign values to variables that might not ordinarily be valid because of differing data type. $cast can be called as either a task or a function.

The syntax for $cast is as follows:
function int $cast( singular dest_var, singular source_exp );
task $cast( singular dest_var, singular source_exp );

The dest_var is the variable to which the assignment is made. The source_exp is the expression that is to be assigned to the destination variable. Use of $cast as either a task or a function determines how invalid assignments are handled. When called as a task, $cast attempts to assign the source expression to the destination variable. If the assignment is invalid, a run-time error occurs, and the destination variable is left unchanged.
typedef enum { red, green, blue, yellow, white, black } Colors;
Colors col;
$cast( col, 2 + 3 ); "

But this was not really enough for me because, I wanted know casting in terms of classes and more importantly what is Down casting? and why at all is it needed?

First, the big question in my mind was define down casting with respect to classes, so here is simple definition:

THE TERM DOWNCAST MEANS GOING DOWN IN THE INHERITANCE TREE.

Here is some conceptual explanation using an example:


class parent;
   virtual function foo;
end 
 
class child extends parent;
   bit foo_child;
   function void foo();
      $display("child with member of values %d",foo_child);
   endfunction
endclass 
 
module foo_bar;
   parent parent_handle;
   parent parent_handle_1;
   child child_handle;
 
   initial begin
      child_handle = new();
      $cast(parent_handle, child_handle);
              // What is the difference between line below and the $cast above ? 
      parent_handle_1 = child_handle; 
      parent_handle_1.foo();
      parent_handle.foo();    
   end
endmodule



$cast is a dynamic cast. That means it will check at run-time if the assignment is legal. A static cast, sometype'(expr), or an assignment is checked for validity at compilation.

The assignment from child_handle to parent_handle is always allowed, but an assignment the reverse direction can only be checked at run-time using $cast. Suppose you had:


initial begin
   parent_handle = new();
   child_handle = parent_handle; // not legal
   $display("I am child with member of value %d", child_handle.foo_bar);
end


The reference to child_handle.foo_bar is only valid if child_handle contains a handle to a child_ class type. Sometimes you do not know what kind of object has been stored in parent_handle because it has been passed down as a function argument. You can use $cast to check the kind of object you have. eg.

...
if ($cast(child_handle, parent_handle)) begin
   $display("I can display %d",child_handle.foo_bar);
else
    $display("I don't have a child_ class type");
...



Lets try to summarize DOWNCASTING in a single sentence:


To access the variables and methods which are only in the subclass and not in the parent class, cast back the object to the subclass handle. As the methods which are added in the subclass and are not in the parent class can't be accessed using the parent class handle.

I guess now down casting makes more sense! :P

Lets have another example to solidify the understanding.

   class parent;
   endclass

   class child extends parent;
      task display ();
         $display("This is amazing");
      endtask
   endclass

   program main ;
      child my_child;
      parent my_parent;
      initial
      begin
         my_child = new();
         my_parent = my_child;
         type_cast(my_parent);
      end
   endprogram
 
   task type_cast(parent my_base);
      child ch;
      $cast(ch,my_base);
      ch.display();
   endtask

RESULT :
This is amazing

2 comments:

  1. //Good tutorial.. But i have few doubts. Please take a took at the below code.


    program main;
    class parent;
    function void print();
    $display("Hello");
    endfunction
    endclass

    class child extends parent;
    function void print1();
    $display("hello 1 printed");
    endfunction
    endclass
    parent p;
    child c=new();
    initial
    begin
    c.print1();
    if( $cast(c,p)) begin
    $display("Can be done");
    c.print1(); end
    else
    $display("this cant be done");
    end
    endprogram

    //c.print1() in the above code prints the same result before and after downcasting. What is the actual use of downcast? I am not able to clearly get it.

    ReplyDelete
    Replies
    1. let me try and explain more explicitly:

      class base;
      int i;
      endclass

      class ext extends base;
      int j;
      endclass

      base a;
      ext b;

      intial begin
      a = new();
      b = new();
      end

      Now lets make some assignments though in real world thsi may be through task arguments (example below later).

      a = b;
      b = null;

      now the parent handle stores a child object. a.i is accesible but compiler wont allow a.j because j is not a member of base but it still is part of ext and thus b which was assigned to a here. So cast is needed to access j $cast(b,a);

      Now to explain how this comes in picture in real life.(keeping my promise of example below later.
      Say you have a queue to hold objects of "base"

      base queue[$];
      task put_element (base queue_element);
      wait(queue.size()<10)
      push_back(queue_element);
      endtask

      Now if I call:
      put_element(b); //where b is object of ext as above

      Here it is effectively queue_element = b; same case I explain with explicit assignment above.

      Delete