Working with @cfunction (and parametric types)

question

#1

To make a package v0.7 deprecation warning free, i recently moved the cfunction s into the @cfunction syntax and got caught on

ERROR: LoadError: could not evaluate cfunction argument type (it might depend on a local variable)

which seems to be correct, the cfunction is

get_stream_callback(T) = @cfunction write_to_stream_callback  Int32 (Ref{T}, Ptr{UInt8}, UInt32)

with T from

function CairoPDFSurface(stream::T, w::Real, h::Real) where {T<:IO}
    callback = get_stream_callback(T)
    ptr = ccall((:cairo_pdf_surface_create_for_stream,_jl_libcairo), Ptr{Nothing},
                (Ptr{Nothing}, Any, Float64, Float64), callback, stream, w, h)
    CairoSurface(ptr, w, h)
end

I understand, that the cfunction macro needs a concrete(?) type, but does this mean i have to rewrite the function - and drop the parametric type?


#2

The only workaround I’m aware of is to make your function an @generated function so that it can produce literal types for ccall. For example:

@generated function foo(x::T) where T
  quote
    ccall(..., ..., (Ref{$T},), x)
  end
end

Edit: sorry, wasn’t paying careful enough attention and mixed up my ccalls and cfunctions. The above is probably not correct.


#3

Perhaps:

get_stream_callback(::Type{T}) where {T} = @cfunction write_to_stream_callback  Int32 (Ref{T}, Ptr{UInt8}, UInt32)

#4

btw: Thank you. There was a race condition as David Anthoff asked the same question also on slack … but the solution converged.


#5

That shouldn’t be necessary for ccall either — the rules for cfunction for v0.7 are now largely brought in line with those for ccall from v0.6. In particular, Kristoffer’s solution is correct for both.