The Crystal Programming Language Forum

Overloading not working?

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.

Define the most specific one first.

Also, please share reproducible code that we can run and verify. Otherwise it’s pretty much guessing on our side. Thank you!

I am working on an example to reproduce this, but still no luck.

You mentioned “Define the most specific one first.”
What are the criteria for a class to be the most specific ?

Example

class DrawElement
end

class DrawPathBase < DrawElement
end

class DrawPathPenBase < DrawPathBase
end

class DrawPath < DrawPathPenBase
end

class DrawFill < DrawPathBase
end

class DrawImage < DrawElement
end

You mean the order in which the overloaded functions are defined.
So in this example I should define the overloaded functions
in this order ?

The most specific to least in this example would be

DrawFill
DrawPath
DrawPathPenBase
DrawPathBsae
DrawImage

Yes.

However, I think the compiler should sort this out. But… until I get some code I can copy and paste into my editor and run it without changes there’s not much I can try.

I made some progress, the order does matter, I just discovered. Moving the code for the base class (DrawElement) somewhere else, where it is “seen” last by the compiler. Some of my tests succeed now, some still fail. Trying to figure out the correct order is the next problem to fix, as this code is generated.

I understand people asking for code to reproduce, but I cannot share code as this is a project I am working on commercially. The only thing I can do is to reproduce the problem and when I do, I will post it on the forum.

For now thanks for your help.