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?
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
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?
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…
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.
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.
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
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:
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.
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.