After reading https://crystal-lang.org/reference/syntax_and_semantics/inheritance.html
and with the help of Blacksmoke16, I changed my code to get rid of most of the case switches.
For more information see Determine type of class
There are 2 major components, one is the interpreter and the second is my graphical library,
both written in Crystal. At the moment I am writing a FFI generator which maps objects
known to the interpreter to Crystal objects and vice versa.
In the graphical library I have defined the following draw element classes
module XYZ
class DrawElement
...
end
class class DrawPathBase < DrawElement
property path : XYZ::Path
...
end
class DrawPathPenBase < DrawPathBase
property pen : Pen
...
end
class DrawPath < DrawPathPenBase
property pen : XYZ::Pen
property path : XYZ::Path
...
end
class DrawImage < DrawElement
property antialias : Bool = false
property transform : XYZ::Transform
property key : String = ""
...
end
... more draw classes ...
class Picture
propery elements : Array(DrawElement)
....
end
end
In the interpreter an user wants to draw some paths
using the following code (not Crystal but in the drawing language I designed)
picture pic;
path p;
p = unitsquare();
draw(pic,p);
shipout();
What this code will do, is construct a path and add this path
to the picture draw elements. The call to shipout will produce
postscript output for each draw element.
In order for this to work I wrote a generator to map these objects.
The generator is part of the specs and is run during tests.
In this simple example, I am only using the mapping
path -> DrawPath and only this path will be added to the picture array elements.
As explained by Blacksmoke16, I am using overloading.
Example code of part of the FFI generator
def DrawElement_to_vminstance(object : XYZ::DrawElement) : VmInstance
vminstance = create_DrawElement_object()
vminstance.fields["id"] = Env::Value.new(object.id)
vminstance.fields["type"] = Env::Value.new(object.type.value)
return vminstance
end
def DrawElement_to_vminstance(object : XYZ::DrawPath) : VmInstance
vminstance = create_DrawPath_object()
vminstance.fields["id"] = Env::Value.new(object.id)
vminstance.fields["pen"] = Env::Value.new(XYZ_pen_to_vminstance(object.pen))
vminstance.fields["path"] = Env::Value.new(XYZ_path_to_vminstance(object.path))
vminstance.fields["type"] = Env::Value.new(object.type.value)
return vminstance
end
... etc ...
#
# Array XYZ::DrawElement -> DrawElement
#
def XYZ_to_DrawElement_arr1(crarr : Array(XYZ::DrawElement) ) : Env::Value
nr_cols = cols(crarr)
vmlist = VmList1.new(33)
(0..nr_cols-1).each do |i|
element = crarr[i]
puts "element #{element.class}" # <<<<<<< class is DrawPath >>>>>>>>
arrayvalue = Env::Value.new(XYZ_DrawElement_to_vminstance(element)) # calls
vmlist.append(arrayvalue)
end
vmarr = Env::Value.new(vmlist)
vmarr.type = 33
return vmarr
end
This should work, but alas it is not. During debugging, the call to XYZ_DrawElement_to_vminstance(element)
with the element of class DrawPath, does not call the correct method.
Instead of calling the function
def XYZ_DrawElement_to_vminstance(object : XYZ::DrawPath) : VmInstance
...
end
its calls the function
def XYZ_DrawElement_to_vminstance(object : XYZ::DrawElement) : VmInstance
...
end
I must be still doing something wrong but I don’t know what. Please advice.