Does SystemVerilog support downcasting (casting a base object to a derived object)? If so, how?
The following downcast example does not work:
class base;
int a = 5;
endclass
class extend extends base;
int b = 1;
endclass
module test;
initial begin
base m_base;
extend m_extend;
m_base = new();
m_extend = new();
$cast(m_extend, m_base);
$display(m_extend.a);
end
endmodule
Modify and rerun the example on EDA Playground: http://www.edaplayground.com/s/4/581
Yes, you can downcast. Your example is the correct syntax, and it actually does compile. However you are getting a runtime error because the cast fails.
The cast in you example is failing because you can only successfully downcast if the handle to the base object is actual referencing an object of the derived type. You can call $cast as a function and it will return a boolean indicating whether the cast was successful or not.
Here is an example:
class animal;
function void eat();
endfunction
endclass
class dog extends animal;
function void bark();
$display("woof");
endfunction
endclass
class cat extends animal;
function void meow();
$display("meow");
endfunction
endclass
module test;
initial begin
dog a_dog = new();
cat a_cat = new();
animal animals[$];
animals.push_back(a_dog);
animals.push_back(a_cat);
foreach (animals[i]) begin
dog another_dog;
animals[i].eat();
if ($cast(another_dog, animals[i])) begin
$display("Found a dog!");
another_dog.bark();
end
end
end
endmodule
Outputs:
# Found a dog!
# woof
See http://www.edaplayground.com/s/474/586
IEEE Std 1800-2012 ยง 8.16 "Casting" states:
It is always legal to assign an expression of subclass type to a variable of a class type higher in the inheritance tree (a superclass or ancestor of the expression type). It shall be illegal to directly assign a variable of a superclass type to a variable of one of its subclass types. However, $cast may be used to assign a superclass handle to a variable of a subclass type provided the superclass handle refers to an object that is assignment compatible with the subclass variable.
The following cast failes because a superclass object not be read as an childclass.
m_base = new();
$cast(m_extend, m_base); // destination type != source object type
To cast correctly the object of the source handle must be compatible with the destination typetypes must be comparable:
m_extend = new();
m_base = m_extend;
$cast(m_extend, m_base); // destination type == source object type
Downcasting can work through levels of inheritance. The following example demonstrates a base class handle pointing a grandchild object being casted to the extend class (the grandchild object's parent class):
class ext_more extends extend;
int c;
endclass
initial begin
base m_base;
extend m_extend;
ext_more m_ext_more;
m_ext_more = new();
m_base = m_ext_more;
$cast(m_extend, m_base); // legal, casting a subclass object to a parent handle
$display(m_extend.a);
end
Here are some working example: http://www.edaplayground.com/s/6/587
In this solution, you are nowhere downcasted, you just use the $cast to check if classes are compatible or not. If yes, print that particular class (DOG in your case) is found.
In you $cast function first parameter (destination) you are taking another_dog as a child class and in the second parameter (source) you are picking from queues that is again queues of child classes
Related
Consider the following code:
class Base {
//some code
};
class A : public virtual Base{
//some code
};
class B : public virtual Base{
//some code
};
class C: public A, public B {
//some code
};
In other words we have multiple inheritance and all classes inherit from class Base. Now consider that the "this" pointer exists in code in classes A,B,C,Base. I know that in general we cannot assume that the "this" pointer value will be identical in all cases. But what it the "this" pointer is cast to the Base* class inside A,B,C whenever it is used. Is it correct to say that the casted pointer "this" after casting to the common Base* class will contain the same address inside A,B,C? Thanks
Yes. The virtual inheritance means that there is only one base class subobject of type Base, which is simultaneously a subobject of the C and the A and the B. A conversion (implicit or by cast) from pointer to any of those types to Base*, or initializing a reference Base& from lvalue of any of those types will cause the compiler to do the correct logic to find that single Base subobject.
But what it the "this" pointer is cast to the Base* class inside A,B,C whenever it is used
Note that there isn't even a need to cast (i.e. convert explicitly). A pointer to derived converts to base pointer implicitly.
Is it correct to say that the casted pointer "this" after casting to the common Base* class will contain the same value inside A,B,C?
A pointer doesn't "contain values" other than the value of the address of an object. The object whose address is stored is the pointed object. A pointer to a base points to the base sub object.
I've got a simple base class and two classes that inherit from it. The problem is that the move assignment operators in each of the child classes aren't being used. Here's basically what I have:
class Parent {
public:
virtual ~Parent();
virtual Parent& operator=(Parent &&parent);
};
class Child1 : public Parent {
public:
~Child1();
Child1& operator=(Child1 &&child);
};
class Child2 : public Parent {
public:
~Child2();
Child2& operator=(Child2 &&child);
};
The implementations are littered with logs, so I know what's being called and when. Then, I run this (pseudo)code:
Parent &p {};
if (x == 1)
p = Child1 {};
else
p = Child2 {};
And I get output that looks something like this:
: Child constructor
: Parent move operator
: Child destruct
: SIGSEGV
Any idea what I'm doing wrong?
This is a classic case of slicing where you are assigning an instance of a derived type to an instance of a base type. To fix this you have to use dynamic allocation and pointers:
std::unique_ptr<Parent> p;
if(x == 1)
p = std::make_unique<Child1>();
else
p = std::make_unique<Child2>();
C++ doesn't work that way. You can't just assign a subclass object to a base class object and expect that to work; this mistake is known as "object slicing".
Also, your if-statement's condition is not comparison, it's assignment.
You have overloaded operator= for the following cases: (pseudocode)
Parent = Parent&&
Child1 = Child1&&
Child2 = Child2&&
However your code then attempts to do Parent = Child1, which wasn't one of those options. Note that the left-hand-side is the class type in which the function is defined - the return value type does not affect function overloading.
There is never implicit conversion from base class to derived class in C++, as that would be too dangerous. (You must use a cast to request that behaviour). For example, in this case it would be wrong to pass a Parent as argument to a function expecting a Child1 because that Parent is not a Child1.
However, there is implicit conversion from derived class to base class reference. So Parent = Child1 will match Parent = Parent&&. It cannot match any of the others because the left-hand-side is not implicitly converted to a derived class.
To solve this problem, your choices include:
Explicitly define Parent = Child1&& and Parent = Child2&& inside Parent (this will need forward declarations)
Have Parent = Parent&& use tag-dispatching or dynamic_cast or otherwise, to achieve the desired behaviour for all of the child classes
As mentioned by others, perhaps your design needs a re-think, as you are slicing on purpose here, but it is rare that slicing is an intended part of an object-oriented design. Possibly you intended Parent p; to actually be a reference or pointer to Parent, instead of an actual Parent.
Let's say I have class SuperClass { public: int a; } and class SubClass : SuperClass { public: int b; } and I took a pointer to an instance of the SubClass SubClass *subPointer and addressed that pointer to a SuperClass pointer SuperClass *superPointer = subPointer. Now of course I can always cast the superPointer object to a pointer of SubClass because the only thing it stores is an adress. But how would I know if the object superPointer is pointing to an instance of SubClass or is just a SuperClass pointer?
You usually don't want to use typeid for this.
You usually want to use dynamic_cast instead:
if (SubClass *p = dynamic_cast<SubClass *>(SuperClassPtr))
// If we get here (the `if` succeeds) it was pointing to an object of
// the derived class and `p` is now pointing at that derived object.
A couple of notes though. First of all, you need at least one virtual function in the base class for this to work (but if it doesn't have a virtual function, why are you inheriting from it?)
Second, wanting this very often tends to indicate design problems with the code. In most cases, you want to define a virtual function in the base class, which you (if necessary) override in the derived class to do whatever's needed so you can just use a pointer to the base class throughout.
Finally, as it stands right now, most of the conversions will fail -- you've used the default (private) inheritance, which prevents the implicit conversion from derived * to base * that you'd normally expect to see happen (you probably want class SubClass : public SuperClass).
Use RTTI machanism. Like:
if(typeid(*superPointer) == typeid(SuperClass)) superPointer->dosomething();
if(typeid(*superPointer) == typeid(SubClass)) superPointer->dosomethingelse();
Here to show you what I mean exactly it hard to describe without code:
class Object
{
// attributes..
};
class Attribute
{
public:
void myfunc();
};
class Character: public Object, public Attribute
{
};
void main()
{
Object* ch = new Character;
// How can I call the myfunc() from Attribute
// tried static_cast<Attribute*>(ch);
}
I only just have a Object Class pointer and i doesn't know
if it is a Character Object or another object which inherit from the
Attribute class, what i know is that the class inherit from Attribute Class.
Cross casting can only be done by dynamic_cast.
Object * o = new Character;
Attribute * a = dynamic_cast<Attribute*>(o);
if (!a) throw_a_fit();
a->myfunc();
However, for this to work you must have polymorphic base classes (they must have at least one virtual function).
If you know that you have an object of the correct type, you can cast explicitly:
Object * ch = /* something that's a Character */
static_cast<Attribute *>(static_cast<Character *>(ch))->myfunc();
Obviously this will not be correct if the type of the most-derived object pointed to by ch is not a subtype of Attribute.
If your class hierarchy is polymorphic (i.e. has at least one virtual function in each base class that you care about), then you can use dynamic_cast directly at runtime and check if it succeeded, but that is a comparatively expensive operation. By contrast, the static cast does not incur any runtime cost at all.
Today I found the following disturbingly ambiguous situation in our code base:
class Base {
public:
virtual void Irrelevant_Function(void) = 0;
protected:
C_Container * Get_Container(void);
};
class A : public Base, public Not_Important {
public:
inline C_Container * Get_Container(void);
};
class B : public Base, protected SomethingElse {
public:
C_Container * Get_Container(void);
};
Many things were calling the Get_Container method, but not always calling the correct one - note that none of these functions were virtual.
I need to rename the methods Get_Base_Container, Get_A_Container, etc to remove the ambiguity. What rules does C++ use to determine which version of a function it should call? I'd like to start from the "known state" of what should have been getting called, and then figure out the bugs from there.
For example, if I have a pointer to a Base and call Get_Container, I assume it would just call the Base version of the function. What if I have a pointer to an A? What about a pointer to a B? What about an A or B on the heap?
Thanks.
It depends how you're calling the function. If you're calling through an A *, an A & or an A, then you'll be calling A::Get_Container(). If you're calling through a Base *, a Base & (even if they point to/reference an A), then you'll be calling Base::Get_Container().
As long as there's no virtual inheritance going on, it's quite easy. If you're working directly with an object, it's the object's method that gets called; if you're working with a pointer or reference, it's the type of the pointer or reference that determines the method, and the type of the object pointed to doesn't matter.
A method is first looked up according to the object's static type. If it is non-virtual there, you're done: that's the method that's called. The dynamic type is what virtual methods, dynamic_cast, and typeid use, and is the "actual" type of the object. The static type is what the static type system works with.
A a; // Static type and dynamic type are identical.
Base &a_base = a; // Static type is Base; dynamic type is A.
a.Get_Contaienr(); // Calls A::Get_Container.
a_base.Get_Container(); // Calls Base::Get_Container.
B *pb = new B(); // Static type and dynamic type of *pb (the pointed-to
// object) are identical.
Base *pb_base = pb; // Static type is Base; dynamic type is B.
pb->Get_Container(); // Calls B::Get_Container.
pb_base->Get_Container(); // Calls Base::Get_Container.
I've assumed above that the protected Base::Get_Container method is accessible, otherwise those will be compile errors.
A couple of additional points to note here:
Name lookup occurs in a single scope; E.g. When calling the method on an object with static type 'B', the compiler considers the interface of 'B' to determine whether or not there is a valid match. If there is not, it only then looks at the interface of Base to find a match. This is why that from the compiler's view, there is no ambiguity and it can resolve the call. If your real code has overloading etc. this may be an issue.
Secondly, it is often forgotten that the 'protected' keyword applies at class and not object level. So for example:
class Base {
protected:
C_Container * Get_Container(void);
};
class B : public Base{
public:
C_Container * Get_Container(void)
{
B b;
// Call the 'protected' base class method on another object.
return b.Base::Get_Container();
}
};