using DataStructures
import Base.isless
struct SEvent{F1}
callback::F1
time::Float64
end
function SEvent(callback::F1, time) where F1
SEvent{F1}(callback, time)
end
function isless(a::SEvent, b::SEvent)
isless(a.time, b.time)
end
Now I try to put SEvent
s into a heap:
s1 = SEvent(10) do x
4
end
s2 = SEvent(3) do x
5
end
a = MutableBinaryMinHeap{Any}() # Or try {SEvent}, the result will be the same
push!(a,s1)
This will cause an error:
ERROR: LoadError: MethodError: Cannot `convert` an object of type
DataStructures.MutableBinaryHeapNode{SEvent{var"#9#10"}} to an object of type
DataStructures.MutableBinaryHeapNode{Any}
Is there no way to put a parametrized struct into a parametrized container like a heap?
Try replacing this with
a = MutableBinaryMinHeap{typeof(s1)}()
This worked for me. If you’ve ever programmed in C++, you can think of Julia parameterized structs like (much easier to use) C++ template classes. The type of object you try to put into the container must match the type of object it’s expecting!
1 Like
why not just something like
MutableBinaryMinHeap([s1])
?
The problem is that I want to put SEvents with different parameters in it. (I.e., SEvents with different callbacks.)
In that case, you might as well just use a container with type Any
.
I did, but it errors:
ERROR: LoadError: MethodError: Cannot `convert` an object of type
DataStructures.MutableBinaryHeapNode{SEvent{var"#9#10"}} to an object of type
DataStructures.MutableBinaryHeapNode{Any}
This seems to be a problem with the implementation of MutableBinaryMinHeap
/MutableBinaryHeapNode
. It works for other containers.
julia> a = Vector{Any}()
0-element Array{Any,1}
julia> push!(a, s1)
1-element Array{Any,1}:
SEvent{getfield(Main, Symbol("##55#56"))}(getfield(Main, Symbol("##55#56"))(), 10.0)
2 Likes
Can confirm @tomerarnon’s point. You can push both s1
and s2
into a Vector{Any}
. Perhaps file an issue in DataStructures.jl?
1 Like
I think the problem happens inside this method. Specially, this line:
push!(nodes, MutableBinaryHeapNode(convert(T, v), i))
because what happens is basically the same as:
julia> struct Wrapper{T}; v :: T; end
julia> vec = Vector{Wrapper{Any}}()
0-element Array{Wrapper{Any},1}
julia> push!(vec, Wrapper(convert(Any, 10)))
ERROR: MethodError: Cannot `convert` an object of type
Wrapper{Int64} to an object of type
Wrapper{Any}
...
In other words, the push!
wraps your value inside a wrapper struct (MutableBinaryHeapNode
more specifically), the code expects that converting your value to the type of the heap (i.e., Any
or SEvents
) they will create a MutableBinaryHeapNode{Any}
(or ...{SEvents}
) and then push it into a vector. However, typeof(convert(Any, s_event_obj))
is typeof(s_event_obj)
not Any
, and therefore the object created is a MutableBinaryHeapNode{typeof(s_event_obj)}
and not a MutableBinaryHeapNode{Any}
and cannot be pushed into a Vector{MutableBinaryHeapNode{Any}}
because it is the wrong type.
I see two workarounds (i.e., not considering a fix in their side):
- You need to use a concrete type instead of an abstract type. What would need you to STOP using a type that is different for each function inside it (i.e., not parametrized by closure). You would need to do:
struct SEvent
callback::Function # or Any
time::Float64
end
- (shameless self-promotion) Use my implementation of a
MutableBinaryHeap
, that I prefer to DataStructures.jl
implementation. It is available in TrackingHeaps.jl (registered package, you can just add
it). It is fully documented, and have come up recently in this thread.
3 Likes