Polymorphism - upcasting & downcasting

Some polymorphic use cases here. Trying to understand whats going on in this code.
A=Base class
B=Derived class

When base class pointer is pointing to derived class object, then it works as expected, except the typeof says the value pointed by the pointer is of type A. I mean, the type of the value pointed by the pointer is being deduced from the pointer type itself which is a bit misleading.

When derived class pointer is pointing to base class object, apart from the typeof behavior mentioned above, I also see its calling the derived object’s method but refers to the base object’s value.

Any explanation on this would be helpful.

class A
  @val : String = "val of A"
  def func : String
    "func of A - " + @val
  end
end

class B < A
  @val : String = "val of B"
  def func : String
    "func of B - " + @val
  end
end

objA = A.new
objB = B.new
ptrA : Pointer(A) = Pointer(A).new(pointerof(objB).address)
ptrB : Pointer(B) = Pointer(B).new(pointerof(objA).address)

puts "objA = objtype:#{typeof(objA)} - 0x#{pointerof(objA).address.to_s(16)} -> #{objA.func}"
# => objA = objtype:A - 0x7fffc72eec70 -> func of A - val of A

puts "objB = objtype:#{typeof(objB)} - 0x#{pointerof(objB).address.to_s(16)} -> #{objB.func}"
# => objB = objtype:B - 0x7fffc72eec68 -> func of B - val of B

puts "ptrA = objtype:#{typeof(ptrA.value)} - 0x#{ptrA.address.to_s(16)} -> #{ptrA.value.func}"
# => ptrA = objtype:A - 0x7fffc72eec68 -> func of B - val of B

puts "ptrB = objtype:#{typeof(ptrB.value)} - 0x#{ptrB.address.to_s(16)} -> #{ptrB.value.func}"
# => ptrB = objtype:B - 0x7fffc72eec70 -> func of B - val of A

Could you clarify in the doc comments, right below the produced value, what do you expect to be printed? With so much output and an explanation far away it’s a bit hard to understand.

That said, I recommend reading the Virtual Types section on the language reference.

Thanks @asterite, I read the Virtual Types section before, let me try to read over all related topics once more.

Breaking down my questions below.
First question/statement can be demonstrated with a simple example even without class/OOP. In the following example, type of the value pointed by pointer is deduced from the pointer type itself, and not from the type of value in the address it is pointing to. I believe this is the expected behavior here as all that is known to the pointer p is the address of the variable being pointed (I have mentioned it is misleading in my previous comment but I will take that back as realistically this is what can be expected here, got it clarified myself with the below simple example). But this use case in my previous post may give more clarity on the second question.

i : UInt32 = 100
p : Pointer(Int32) = Pointer(Int32).new(pointerof(i).address)
puts typeof(p.value)    # => Int32

Second question is the real question as such. Assuming B is child class of A, and B has redefined all the attributes & methods of A, with the following example, whose attributes & methods is to be invoked when ptrB.value.anything is called? Here objA is instance of A.

ptrB : Pointer(B) = Pointer(B).new(pointerof(objA).address)
Based on the o/p I have posted, ptrB.value refers to attribute of A (I can agree with this behavior) but method of B (I expected it would call method of A).

You are taking a pointer of A and putting it in a pointer of B. That’s undefined behavior. If something works, it’s pure luck or coincidence.

Thank you @asterite
I was trying to simulate the run time polymorphism use case of C++ here.
After your answer I was thinking this is fine with crystal as class instances are passed by reference to other functions and hence the use case is taken care of (no necessity to do all the pointer address based initiation that I have tried). Then thought about the case of a struct that is passed by value to a function, then remembered the rule “A struct cannot inherit from a non-abstract struct.” and this all makes sense now. This is great!

C++ virtual dispatch is done with virtual tables. In Crystal it’s not like that (there’s a switch) so if you try to copy C++ it won’t work.

1 Like