JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.43k stars 5.45k forks source link

Splicing in arguments into a ccall #1315

Open timholy opened 11 years ago

timholy commented 11 years ago

Conceptually related to #1313.

If I try defining a function this way:

@eval begin
    function mylseek(args...)
        cpos = ccall(:lseek, FileOffset, (Int32, FileOffset, Int32), args...)
    end
end

then I get:

julia> s = open("h5test.c")
IOStream(<file h5test.c>)

julia> mylseek(fd(s), 5, 0)
error compiling mylseek: ccall: wrong number of arguments to C function

One workaround is the following:

ccallargs = Any[:(:lseek), FileOffset, expr(:tuple, Any[Int32, FileOffset, Int32]), :(args[1]), :(args[2]), :(args[3])]
ex = expr(:ccall, ccallargs)
@eval begin
    function mylseek(args...)
        $ex
    end
end

julia> mylseek(fd(s), 11, 0)
11

Perhaps there is a less-awkward syntax? If not, should we add a ccallexpr function to the library that would build those expressions?

JeffBezanson commented 11 years ago

I believe this can't be done portably except for functions taking a va_list, and even then it's tricky. In your example the metaprogramming is unnecessary; you can use a varargs julia function with an "if" case for various lengths, and pass args[1], args[2], etc. to the ccall.

timholy commented 11 years ago

I found a way to do what I wanted, although I'm sure I'm handling a much more specific case than you're envisioning. Would you please check https://github.com/timholy/julia_hdf5/blob/master/hdf5.jl and see whether the section marked ### Utilities for generating ccall wrapper functions programmatically ### has anything of general interest? If not, it can continue to live in the HDF5 wrapper.

vtjnash commented 11 years ago

previously mentioned code now now lives at https://github.com/timholy/HDF5.jl/blob/master/src/plain.jl#L1443

stevengj commented 11 years ago

I had a related problem: wanted to ccall a varargs function with arguments (Int, Ptr{Void}...) by splicing in the argument list.

There seems to be some disagreement about whether it is portable to assume that varargs are passed in the same way as ordinary arguments, although apparently LLVM assumes (assumed?) this: see here and here, for example.

Socob commented 1 year ago

Potentially relevant for this issue: I’ve created the Ccalls.jl package based on a discussion on Discourse that allows making FFI calls like ordinary function calls, i. e. including “splat” operations:

julia> using Ccalls

julia> args = (2, 10)
(2, 10)

julia> Ccall(:pow, Cdouble, (Cdouble, Cdouble), args...)
1024.0

So using current Julia, it is actually possible to do this!