Controlling the type of anonymous functions

I have a function with the following form:

function make_func()
	return x -> x*10
end

I would like to change it so that it will return a function that takes Cint and returns Cint, in order to pass it to C code as a callback using ccall. I expected this to work, but it doesn’t. (It gives a syntax error, unless I remove the type assertion on the parameter x.)

function make_func()
	f = function(x::Cint)::Cint
		return x*10
	end
	return f
end

Is there a way that I can specify both the input and return types of an anonymous function?

Edit: I misread the question. The post below is probably not very helpful, sorry.

Original post:

You don’t actually need to restrict the anonymous function at all. Instead, you should use @cfunction to generate the C-compatible function pointer:

julia> function make_func()
         x -> x * 10
       end
make_func (generic function with 1 method)

julia> @cfunction(make_func, Cint, (Cint,))
Ptr{Nothing} @0x00007f14ade9a230

See Calling C and Fortran Code · The Julia Language

1 Like

I’m having a hard time making sense of that. I don’t want to turn make_func into a cfunction, I want to turn the function returned by make_func into a cfunction, i.e.

func = make_func()
func_c = @cfunction($func, Cint, (Cint,))

If I try to pass make_func to @cfunction directly, it gives the error I would expect:

ERROR: LoadError: MethodError: no method matching make_func(::Int32)

I note also that I can get around this by doing the following:


	func = make_func()

	# this function exists only to control the type of func()
	function func_wrapper(x::Cint)::Cint
		return func(x)
	end

	func_wrapper_c = @cfunction($func_wrapper, Cint, (Cint,))

This appears to work correctly, and to behave as a C function of type int (*f)(int), as expected. But it’s weird and hacky to have to create the func_wrapper function just to control the type of the anonymous function. I’m asking how to do that in a more paradigmatic way.

Looks like the anonymous function form is bugged in how it parses the return type assertions — it puts them on the argument instead! Tracked as issue #32557:

julia> :(function (x)::Cint end)
:(function (x::Cint,)
      #= REPL[39]:1 =#
  end)

As a workaround, you can just use a named function instead — it’s still scoped inside your function and forms a closure if you capture any variables. Or you can just manually convert things to Cint yourself when you return — the function return declaration is just a handy way to annotate that.

2 Likes

Great, many thanks. Thanks for the tip about named functions! So here is working code that does what I wanted:

function make_func()
	function f(x::Cint)::Cint
		x*10
	end
	return f
end

function main()

	func = make_func()

	func_wrapper_c = @cfunction($func, Cint, (Cint,))
	result = ccall((:myTestFunction, "myTestLib"), Int32, (Ptr{Cvoid},), func_wrapper_c)

	println(result)

end

main()