Parameterized class: specialization

In C++ template specialization allows you to parameterize a class, but have ‘special’ cases for particular cases.

Note: Looking into the SV LRM , they refer to specialziation as just assigning the unique parameters. That it not the meaning of specialization I am talking about here.

I would like to do for example:

class C #(type T);
    T value=0;
    function void do_it();
        T=T+1;
     endfunction
endclass

So do_it increments by 1, but in the special case of T being a ‘real’ do_it should do something else, like subtract 1. E.g. something like:

class C #(real);     // <---- no valid SV syntax, means specialization for real
    real value=0;
    function void do_it();
        T=T-1;
     endfunction
endclass

so that I could do for example:

C#(int) I;
C#(real) R;

I.doit();  // value would be +1
R.doit();  // value would be -1

I looked in the LRM but at first sight this does not seem possible?
Any suggestions for arriving to similar behaviour?

Template specialization isn’t possible in SV. The only workaround I can think of is:


class C_real extends C #(real);
    function void do_it();
        value = value - 1;
     endfunction
endclass

In you’re code you’d have to instantiate an object of type C_real instead of C #(real).

What you are asking for does not exist in SV. But is it possible to query a type or type of a variable.

module top;
class C#(type T);
   T value;
   function void doit;
      if (type(T)==type(real))
	value--;
      else
	value++;
   endfunction
endclass : C
   C#(int) I=new;;
   C#(real) R=new;;
   initial begin
      I.doit;
      R.doit;
      $display(I.value,, R.value);
   end
endmodule : top

In reply to dave_59:

I wasnt aware of the type() operator. In practice the types will be classes.
I just looked into the LRM, I assume if I have a base class and several derived classes, none of these would be matching type amongst eachother?

In reply to NiLu:

Correct, but then you should be using virtual methods.

In reply to dave_59:

A note on this solution: I know of at least one simulator that doesn’t support using type(…) in procedural code.

An issue with the type() operator solution is that when the ‘template specialization’ does not just change some operation or so, but needs to call functions that return the type T. E.g. as follows (pseudo code):

class t_Real;      // real samples
class t_Complex;   // complex samples

function getNext_Real(t_Real x);
...
endfunction 

function getNext_Complex(t_Complex x);
...
endfunction

class C#(type T);
   T value;
   function doit(T x);
      if (type(T)==type(t_Real)) begin
        getNext_Real(x);
      end else if (type(T)==type(t_Complex))
        getNext_Complex(x);
      end
   endfunction
endclass

C#(t_Real) R=new;
t_Real x;
R.doit(x);

This doesn’t work because in the function doit x is of a certain type, but both getNext_Real(x) and getNext_Complex(x) is written. I do understand this, but you could also argue that actually always only ‘the correct one’ will be called. but I assume SV doesnt ‘think’ that way.

Any clever workarounds? I am currently thinking in the direction of having t_Real and t_Complex be derived from a common base class, and call the functions with the base class, and then $cast it or something like that. But if I do so, I could maybe avoid the parameterization alltogether…

In reply to NiLu:

I ran into exactly the same problem. Think of it this way: choosing which method you call is something that is done at run time. Parsing the code is something that is done at compile time, when you don’t know which branch of the code will be taken. This is why the compiler complains with your code.

In reply to Tudor Timi:

If you ran into the same problem, what did you do as solution? Using polymorphism instead of templates?

Personally, I find it a pity that SV templates (parameters) have several limitations:

  • no template specialization
  • no function templates (without wrapping in a class)
    Also the lack of plain function overloading is sometimes annoying me.

I wonder if any of these will be added in the future…

In reply to NiLu:

It won’t apply to you, because I was calling some DPI imported functions (in the places you’re calling get_next_real/complex) and I changed it to use VPI system tasks, which aren’t strongly typed.

I guess in your case, using a number base class does make the most sense. You’d just call something like get_next_number(x) and the compiler shouldn’t have any problems. x would still typed by the parameter. This way you combine polymorphism with templates.

class number;
  // API that defines what a number can do
endclass

class real extends number;
  // extra stuff for a real
endclass

class complex extends number;
  // extra stuff for a complex
endclass

Like you said, whether it makes sense to keep the parameterized class in this case, I’m not sure. It would probably strongly depend on your use case.

In reply to Tudor Timi:

What do you mean with call ‘get_next_number(x)’? What function prototype would that have? How does it know if x was a real or complex?
The get_next_* function has to call the derived class ‘extra stuff’.

what does work for now is:

class C#(type T);
   T value;
   function doit(T x);
      if (type(T)==type(t_Real)) begin
        t_Real tmp;
        getNext_Real(tmp);
        $cast(x,tmp);
      end else if (type(T)==type(t_Complex))
        t_Complex tmp;
        getNext_Complex(tmp);
        $cast(x,tmp);
      end
   endfunction
endclass

But it doesn’t feel very elegant.

In reply to NiLu:

I don’t know what you’re doing in your get_next_complex/real(…) functions, but if you were to call any methods on the object on the argument you pass to them, then you don’t need to care whether that argument is real or complex as long as you’re using virtual methods:


virtual class number;
  virtual function void do_something();
endclass

class real_number extends number;
  virtual function void do_something();
    $display("doing something on a real");
  endfunction
endclass

class complex_number extends number;
  virtual function void do_something();
    $display("doing something on a complex");
  endfunction
endclass

This means that whenever you call one of these functions on a handle of type number the one corresponding to the appropriate type will get called:


function void get_next_number(number num);
  num.do_something();
endfunction


// ...
real_number real_num = new();
get_next_number(real_num);

complex_number complex_num = new();
get_next_number(complex_num );

The first call will print “doing something on a real”, while the second will print “doing something on a complex”. You don’t have to do any casting this way.

In reply to Tudor Timi:

The issue is that the getnext_real and getnext_complex are very different, and it would be difficult to implement solely via virtual functions of the base class. That is why I wanted the template specialization in the first place. If all to be called methods were to exist for the real and complex class the simple parametrization would have worked fine.

Personally I don’t get it why pretty simple mechanisms like function overloading and parameter (template) specialization are not present in the SV language. I can’t imagine it is so hard to add that to the compiler.

In reply to NiLu:

In that case, maybe you should consider subclassing your “C” class (the one you were parameterizing) and using polymorphism on that one.