Cast to parameterized variable fails

Below code throws error when simulation is loaded, if right-hand side of cast operation is not given as class type.
I would like it to work with types of class or other data types such as struct. When given type is struct, cast statement gives error saying arguments should be singular types. How can I make this code to work with both types?


class param_class #(type type_foo = some_def_type,
                    type type_bar = some_def_type);

  type_foo foo;
  type_bar bar;

  func void some_func();

    $cast(foo, bar);

  endfunction

endclass



In reply to emin:

$cast can only be used on singular data types. The LRM provides the following definition:

singular: An expression, data object, or data type that represents a single value, symbol, or handle. A singular data type is any data type except an unpacked structure, unpacked union, or unpacked array data type.

You cannot use $cast on unpacked structures.

I know its illegal to $cast to non-singular data type by LRM.

$cast( singular dest_var, singular source_exp );

Asking for a workaround or a solution to make this code to work with non-singular data types, obviously it should be without $cast.

In reply to emin:

Without knowing exactly what you are trying to accomplish, it’s difficult to provide an answer.

When you instantiate param_class, you should know the two types that you are using as parameters. You could have two different functions, one that uses $cast and one that uses assignments, and use the appropriate version.


class param_class #(type type_foo = some_def_type,
                    type type_bar = some_def_type,
                    bit is_singular = 1);
  type_foo foo;
  type_bar bar;
 
  function void some_func_singular();
    $cast(foo, bar);
  endfunction

  function void some_func_non_singular();
    foo = bar;
  endfunction

  function void some_func();
    if (is_singular) some_func_singular();
    else some_func_non_singular();
  endfunction
 
endclass

In reply to cgales:

Actually that won’t work because the function need to pass compilation even if they are never used. But you can achieve something similar by using what called a strategy or policy design pattern. It essentially works by wrapping functionality in an object specific to the types you need.

module top;
  typedef struct {int A;} some_def_type;
  class A;
  endclass

  interface class policies #(type from_type, to_type);
    pure virtual function void copy(input from_type from, output to_type to);
    pure virtual function logic compare(input from_type lhs, output to_type rhs);
  endclass
    
  class policy_struct#(type from_type, to_type) implements policies#(from_type,to_type);
      virtual function void copy(input from_type from, output to_type to);
        to = to_type'(from);
      endfunction
      virtual function logic compare(input from_type lhs, output to_type rhs);
        return lhs == to_type'(rhs);
      endfunction
  endclass
  class policy_class#(type from_type, to_type) implements policies#(from_type,to_type);
      virtual function void copy(input from_type from, output to_type to);
        $cast(to,from);
      endfunction
      virtual function logic compare(input from_type lhs, output to_type rhs);
        return 0; // did not implement yet
      endfunction
  endclass
    
    
  class param_class #(type type_foo = some_def_type,
                    type type_bar = some_def_type);
    type_foo foo;
    type_bar bar;
    policies#(type_foo,type_bar) policy;
  
    function void some_func();
      policy.copy(bar,foo);
    endfunction
   endclass
      
   param_class#(some_def_type,some_def_type)   h1 = new;
   policy_struct#(some_def_type,some_def_type) h2  = new;
   initial begin
      h1.policy=h2;
      h1.some_func;
   end
endmodule

In reply to dave_59:

Thanks for the solution. I will walk through the attached article but,

How about the below implementation which makes use of polymorphism without interface classes?
What is the difference? How to choose in between?

module top;
      typedef struct {int A;} some_def_type;
      class A;
      endclass

      virtual class abstract_policy#(type from_type, to_type);
          virtual function void copy(input from_type from, output to_type to);
          endfunction
        
	  virtual function logic compare(input from_type lhs, output to_type rhs);
          endfunction
      endclass
      
      class policy_struct#(type from_type, to_type) extends abstract_policy#(from_type, to_type);
        
        virtual function void copy(input from_type from, output to_type to);
              to = to_type'(from);
              $display("policy_struct");
      	endfunction
        
      endclass
      
      
      class policy_class#(type from_type, to_type) extends abstract_policy#(from_type, to_type);
          virtual function void copy(input from_type from, output to_type to);
            $cast(to,from);
            $display("policy_class");
          endfunction
      endclass
      
 
 
  class param_class #(type type_foo = some_def_type,
                    type type_bar = some_def_type,
                      type policy_t = abstract_policy#(type_foo, type_bar));
    type_foo foo;
    type_bar bar;
   	policy_t policy;
    
    function new();
      policy = new(); 
    endfunction
 
    function void some_func();
      policy.copy(bar,foo);
    endfunction
   endclass
 
   param_class#(some_def_type,
                some_def_type,
                policy_struct#(some_def_type, some_def_type)) h1 = new;
      
      param_class#(A,A, policy_class#(A, A)) h2 = new;
      

   initial begin
     h1.some_func();
     h2.some_func();
   end

In reply to emin:

You should use in interface class anywhere you have a abstract/virtual class with only pure virtual methods. An interface class gives you access to multiple inheritance. A single class can implement multiple interface classes as well as be extended from another base class.

You can break up your policies into individual interface classes and then group them together as needed. For example suppose you had policies for create, compare, copy, and clone (create followed by copy). A class policy might implement all 4 policies, but a struct policy would only implement copy and compare. As you define more policies for a broader range of types, the end user class only needs to declare interface class variables for policies that it needs, and will only work with types that have defined the policies it needs. The paper goes into this in more detail.

Also see this link for more use cases.