From 59743f1395cf899b49f27d9978b465d2a779a73e Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Thu, 11 Apr 2019 21:10:17 -0700 Subject: [PATCH 1/7] Update calling-c-and-fortran docs * Formatting, wording updates --- doc/src/manual/calling-c-and-fortran-code.md | 189 ++++++++++--------- 1 file changed, 100 insertions(+), 89 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index a73f746f8ceaa..c68f03b906cde 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -11,16 +11,12 @@ The code to be called must be available as a shared library. Most C and Fortran compiled as shared libraries already, but if you are compiling the code yourself using GCC (or Clang), you will need to use the `-shared` and `-fPIC` options. The machine instructions generated by Julia's JIT are the same as a native C call would be, so the resulting overhead is the same -as calling a library function from C code. (Non-library function calls in both C and Julia can -be inlined and thus may have even less overhead than calls to shared library functions. When both -libraries and executables are generated by LLVM, it is possible to perform whole-program optimizations -that can even optimize across this boundary, but Julia does not yet support that. In the future, -however, it may do so, yielding even greater performance gains.) +as calling a library function from C code. [^1] Shared libraries and functions are referenced by a tuple of the form `(:function, "library")` -or `("function", "library")` where `function` is the C-exported function name. `library` refers -to the shared library name: shared libraries available in the (platform-specific) load path will -be resolved by name, and if necessary a direct path may be specified. +or `("function", "library")` where `function` is the C-exported function name, and `library` refers +to the shared library name. Shared libraries available in the (platform-specific) load path will +be resolved by name. The full path to the library may also be specified. A function name may be used alone in place of the tuple (just `:function` or `"function"`). In this case the name is resolved within the current process. This form can be used to call C library @@ -37,30 +33,33 @@ arrays and other mutable objects which are normally heap-allocated, but also to scalar values such as integers and floats which are normally stack-allocated and commonly passed in registers when using C or Julia calling conventions. -Finally, you can use [`ccall`](@ref) to actually generate a call to the library function. Arguments -to [`ccall`](@ref) are as follows: +Finally, you can use [`ccall`](@ref) to actually generate a call to the library function. The arguments +to [`ccall`](@ref) are: -1. A `(:function, "library")` pair, which must be written as a literal constant, +1. A `(:function, "library")` pair, OR - a `:function` name symbol or `"function"` name string, which is resolved in the - current process, + a `:function` name symbol or `"function"` name string, OR a function pointer (for example, from `dlsym`). -2. Return type (see below for mapping the declared C type to Julia) +2. The function's return type - * This argument will be evaluated at compile-time, when the containing method is defined. +3. A tuple of input types, corresponding to the function signature -3. A tuple of input types. The input types must be written as a literal tuple, not a tuple-valued - variable or expression. +4. The actual argument values to be passed to the function, if any; each is a separate parameter. - * This argument will be evaluated at compile-time, when the containing method is defined. +!!! note + The `(:function, "library")` pair, return type, and input types must be literal constants + (i.e., they can't be variables, but see [Non-constant Function Specifications](@ref) below). + + The remaining parameters are evaulated at compile time. -4. The following arguments, if any, are the actual argument values passed to the function. +!!! note + See below for how to [map C types to Julia types](@ref mapping-c-types-to-julia). As a complete but simple example, the following calls the `clock` function from the standard C library: @@ -164,20 +163,20 @@ typedef returntype (*functiontype)(argumenttype, ...) ``` The macro [`@cfunction`](@ref) generates the C-compatible function pointer for a call to a -Julia function. Arguments to [`@cfunction`](@ref) are as follows: +Julia function. The arguments to [`@cfunction`](@ref) are: 1. A Julia Function -2. Return type +2. The return type 3. A literal tuple of input types -Like ccall, all of these arguments will be evaluated at compile-time, when the containing method is defined. +As with `ccall`, all of these arguments will be evaluated at compile-time when the containing method is defined. Currently, only the platform-default C calling convention is supported. This means that `@cfunction`-generated pointers cannot be used in calls where WINAPI expects `stdcall` function on 32-bit windows, but can be used on WIN64 (where `stdcall` is unified with the C calling convention). -A classic example is the standard C library `qsort` function, declared as: +As a classic example, consider the standard C library `qsort` function, declared as: ```c void qsort(void *base, size_t nmemb, size_t size, @@ -187,10 +186,11 @@ void qsort(void *base, size_t nmemb, size_t size, The `base` argument is a pointer to an array of length `nmemb`, with elements of `size` bytes each. `compare` is a callback function which takes pointers to two elements `a` and `b` and returns an integer less/greater than zero if `a` should appear before/after `b` (or zero if any order -is permitted). Now, suppose that we have a 1d array `A` of values in Julia that we want to sort +is permitted). + +Now, suppose that we have a 1d array `A` of values in Julia that we want to sort using the `qsort` function (rather than Julia's built-in `sort` function). Before we worry about -calling `qsort` and passing arguments, we need to write a comparison function that works for some -arbitrary objects (which define `<`): +calling `qsort` and passing arguments, we need to write a comparison function: ```jldoctest mycompare julia> function mycompare(a, b)::Cint @@ -199,8 +199,8 @@ julia> function mycompare(a, b)::Cint mycompare (generic function with 1 method) ``` -Notice that we have to be careful about the return type: `qsort` expects a function returning -a C `int`, so we annotate the return type of the function to be sure it returns a `Cint`. +``qsort`` expects a comparison function that return a C ``int``, so we annotate the return type +to be ``Cint``. In order to pass this function to C, we obtain its address using the macro `@cfunction`: @@ -234,12 +234,14 @@ julia> A ``` As can be seen, `A` is changed to the sorted array `[-2.7, 1.3, 3.1, 4.4]`. Note that Julia -knows how to convert an array into a `Ptr{Cdouble}`, how to compute the size of a type in bytes -(identical to C's `sizeof` operator), and so on. For fun, try inserting a `println("mycompare($a, $b)")` -line into `mycompare`, which will allow you to see the comparisons that `qsort` is performing -(and to verify that it is really calling the Julia function that you passed to it). +[takes care of converting the array to a `Ptr{Cdouble}`](@ref automatic-type-conversion)), computing +the size of the element type in bytes, and so on. -## Mapping C Types to Julia +For fun, try inserting a `println("mycompare($a, $b)")` line into `mycompare`, which will allow +you to see the comparisons that `qsort` is performing (and to verify that it is really calling +the Julia function that you passed to it). + +## [Mapping C Types to Julia](@id mapping-c-types-to-julia) It is critical to exactly match the declared C type with its declaration in Julia. Inconsistencies can cause code that works correctly on one system to fail or produce indeterminate results on @@ -247,10 +249,9 @@ a different system. Note that no C header files are used anywhere in the process of calling C functions: you are responsible for making sure that your Julia types and call signatures accurately reflect those in the C header -file. (The [Clang package](https://github.com/ihnorton/Clang.jl) can be used to auto-generate -Julia code from a C header file.) +file.[^2] -### Auto-conversion: +### [Automatic Type Conversion](@id automatic-type-conversion) Julia automatically inserts calls to the [`Base.cconvert`](@ref) function to convert each argument to the specified type. For example, the following call: @@ -259,26 +260,16 @@ to the specified type. For example, the following call: ccall((:foo, "libfoo"), Cvoid, (Int32, Float64), x, y) ``` -will behave as if the following were written: +will behave as if the following were written[^3]: ```julia ccall((:foo, "libfoo"), Cvoid, (Int32, Float64), - Base.unsafe_convert(Int32, Base.cconvert(Int32, x)), - Base.unsafe_convert(Float64, Base.cconvert(Float64, y))) + Base.convert(Int32, x), Base.convert(Float64, y)) ``` -[`Base.cconvert`](@ref) normally just calls [`convert`](@ref), but can be defined to return an -arbitrary new object more appropriate for passing to C. -This should be used to perform all allocations of memory that will be accessed by the C code. -For example, this is used to convert an `Array` of objects (e.g. strings) to an array of pointers. - -[`Base.unsafe_convert`](@ref) handles conversion to [`Ptr`](@ref) types. It is considered unsafe because -converting an object to a native pointer can hide the object from the garbage collector, causing -it to be freed prematurely. - -### Type Correspondences: +### Type Correspondences -First, a review of some relevant Julia type terminology: +First, let's review some relevant Julia type terminology: | Syntax / Keyword | Example | Description | |:----------------------------- |:------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -347,7 +338,7 @@ This can help for writing portable code (and remembering that an `int` in C is n an `Int` in Julia). -**System Independent:** +**System Independent Types** | C name | Fortran name | Standard Julia Alias | Julia Base Type | |:------------------------------------------------------- |:------------------------ |:-------------------- |:-------------------------------------------------------------------------------------------------------------- | @@ -388,7 +379,7 @@ to skip the check, you can use `Ptr{UInt8}` as the argument type. `Cstring` can the [`ccall`](@ref) return type, but in that case it obviously does not introduce any extra checks and is only meant to improve readability of the call. -**System-dependent:** +**System Dependent Types** | C name | Standard Julia Alias | Julia Base Type | |:--------------- |:-------------------- |:-------------------------------------------- | @@ -471,7 +462,7 @@ checks and is only meant to improve readability of the call. !!! note A C function declared to return `Cvoid` will return the value `nothing` in Julia. -### Struct Type correspondences +### Struct Type Correspondences Composite types, aka `struct` in C or `TYPE` in Fortran90 (or `STRUCTURE` / `RECORD` in some variants of F77), can be mirrored in Julia by creating a `struct` definition with the same @@ -485,24 +476,27 @@ are not possible in the translation to Julia. Packed structs and union declarations are not supported by Julia. -You can get a near approximation of a `union` if you know, a priori, the field that will have +You can get an approximation of a `union` if you know, a priori, the field that will have the greatest size (potentially including padding). When translating your fields to Julia, declare the Julia field to be only of that type. -Arrays of parameters can be expressed with `NTuple`: +Arrays of parameters can be expressed with `NTuple`. For example, this struct in C, -in C: ```c struct B { int A[3]; }; + b_a_2 = B.A[2]; ``` -in Julia: + +can be written in Julia as + ```julia struct B A::NTuple{3, Cint} end + b_a_2 = B.A[3] # note the difference in indexing (1-based in Julia, 0-based in C) ``` @@ -602,7 +596,7 @@ In Julia code wrapping calls to external C routines, ordinary (non-pointer) data to be of type `T` inside the [`ccall`](@ref), as they are passed by value. For C code accepting pointers, [`Ref{T}`](@ref) should generally be used for the types of input arguments, allowing the use of pointers to memory managed by either Julia or C through the implicit call to [`Base.cconvert`](@ref). - In contrast, pointers returned by the C function called should be declared to be of output type +In contrast, pointers returned by the C function called should be declared to be of output type [`Ptr{T}`](@ref), reflecting that the memory pointed to is managed by C only. Pointers contained in C structs should be represented as fields of type `Ptr{T}` within the corresponding Julia struct types designed to mimic the internal structure of corresponding C structs. @@ -718,37 +712,7 @@ ccall(:foo, Cvoid, (Ref{Cint}, Ref{Cfloat}), width, range) Upon return, the contents of `width` and `range` can be retrieved (if they were changed by `foo`) by `width[]` and `range[]`; that is, they act like zero-dimensional arrays. -### Special Reference Syntax for ccall (deprecated): - -The `&` syntax is deprecated, use the `Ref{T}` argument type instead. - -A prefix `&` is used on an argument to [`ccall`](@ref) to indicate that a pointer to a scalar -argument should be passed instead of the scalar value itself (required for all Fortran function -arguments, as noted above). The following example computes a dot product using a BLAS function. - -```julia -function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) - @assert length(DX) == length(DY) - n = length(DX) - incx = incy = 1 - product = ccall((:ddot_, "libLAPACK"), - Float64, - (Ref{Int32}, Ptr{Float64}, Ref{Int32}, Ptr{Float64}, Ref{Int32}), - n, DX, incx, DY, incy) - return product -end -``` - -The meaning of prefix `&` is not quite the same as in C. In particular, any changes to the referenced -variables will not be visible in Julia unless the type is mutable (declared via `mutable struct`). However, -even for immutable structs it will not cause any harm for called functions to attempt such modifications -(that is, writing through the passed pointers). Moreover, `&` may be used with any expression, -such as `&0` or `&f(x)`. - -When a scalar value is passed with `&` as an argument of type `Ptr{T}`, the value will first be -converted to type `T`. - -## Some Examples of C Wrappers +## C Wrapper Examples Here is a simple example of a C wrapper that returns a `Ptr` type: @@ -855,6 +819,25 @@ external C function. As a result, the code may produce a memory leak if `result_ freed by the garbage collector, or if the garbage collector prematurely frees `result_array`, the C function may end up throwing an invalid memory access exception. +## Fortran Wrapper Example + +The following example computes a dot product using a Fortran BLAS function. Note the use +of `Ref` and `Ptr`. + +```julia +function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) + @assert length(DX) == length(DY) + n = length(DX) + incx = incy = 1 + product = ccall((:ddot_, "libLAPACK"), + Float64, + (Ref{Int32}, Ptr{Float64}, Ref{Int32}, Ptr{Float64}, Ref{Int32}), + n, DX, incx, DY, incy) + return product +end +``` + + ## Garbage Collection Safety When passing data to a [`ccall`](@ref), it is best to avoid using the [`pointer`](@ref) function. @@ -1079,3 +1062,31 @@ For more details on how to pass callbacks to C libraries, see this [blog post](h For direct C++ interfacing, see the [Cxx](https://github.com/Keno/Cxx.jl) package. For tools to create C++ bindings, see the [CxxWrap](https://github.com/JuliaInterop/CxxWrap.jl) package. + + + +[^1]: Non-library function calls in both C and Julia can be inlined and thus may have + even less overhead than calls to shared library functions. When both libraries and executables are + generated by LLVM, it is possible to perform whole-program optimizations that can even optimize + across this boundary, but Julia does not yet support that. In the future, however, it may do so, + yielding even greater performance gains. + +[^2]: The [Clang package](https://github.com/ihnorton/Clang.jl) can be used to auto-generate Julia code + from a C header file. + +[^3]: For the conversion of ccall parameters, the actual conversion would look something like this: + + ```julia + ccall((:foo, "libfoo"), Cvoid, (Int32, Float64), + Base.unsafe_convert(Int32, Base.cconvert(Int32, x)), + Base.unsafe_convert(Float64, Base.cconvert(Float64, y))) + ``` + + [`Base.cconvert`](@ref) normally just calls [`convert`](@ref), but can be defined to return an + arbitrary new object more appropriate for passing to C. + This should be used to perform all allocations of memory that will be accessed by the C code. + For example, this is used to convert an `Array` of objects (e.g. strings) to an array of pointers. + + [`Base.unsafe_convert`](@ref) handles conversion to [`Ptr`](@ref) types. It is considered unsafe because + converting an object to a native pointer can hide the object from the garbage collector, causing + it to be freed prematurely. From 63129e537b12943b29cb55500063129ea092a6a4 Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Tue, 16 Apr 2019 00:25:44 -0700 Subject: [PATCH 2/7] Re-clarify Ref/Ptr documentation * Revery the creation of footnote 3. Part of that note was put back into the text, and part was deleted, as it was pointed out that it was misleading. * Remove some additional misleading comments regarding the use of Ptr and Ref. --- doc/src/manual/calling-c-and-fortran-code.md | 58 +++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index c68f03b906cde..62d2c0ade2a3e 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -260,13 +260,23 @@ to the specified type. For example, the following call: ccall((:foo, "libfoo"), Cvoid, (Int32, Float64), x, y) ``` -will behave as if the following were written[^3]: +will behave as if the following were written: ```julia ccall((:foo, "libfoo"), Cvoid, (Int32, Float64), - Base.convert(Int32, x), Base.convert(Float64, y)) + Base.unsafe_convert(Int32, Base.cconvert(Int32, x)), + Base.unsafe_convert(Float64, Base.cconvert(Float64, y))) ``` +[`Base.cconvert`](@ref) normally just calls [`convert`](@ref), but can be defined to return an +arbitrary new object more appropriate for passing to C. +This should be used to perform all allocations of memory that will be accessed by the C code. +For example, this is used to convert an `Array` of objects (e.g. strings) to an array of pointers. + +[`Base.unsafe_convert`](@ref) handles conversion to [`Ptr`](@ref) types. It is considered unsafe because +converting an object to a native pointer can hide the object from the garbage collector, causing +it to be freed prematurely. + ### Type Correspondences First, let's review some relevant Julia type terminology: @@ -742,12 +752,12 @@ function `gsl_permutation_alloc`. As user code never has to look inside the `gsl struct, the corresponding Julia wrapper simply needs a new type declaration, `gsl_permutation`, that has no internal fields and whose sole purpose is to be placed in the type parameter of a `Ptr` type. The return type of the [`ccall`](@ref) is declared as `Ptr{gsl_permutation}`, since -the memory allocated and pointed to by `output_ptr` is controlled by C (and not Julia). +the memory allocated and pointed to by `output_ptr` is controlled by C. The input `n` is passed by value, and so the function's input signature is simply declared as `(Csize_t,)` without any `Ref` or `Ptr` necessary. (If the wrapper was calling a Fortran function instead, the corresponding function input -signature should instead be `(Ref{Csize_t},)`, since Fortran variables are +signature would instead be `(Ref{Csize_t},)`, since Fortran variables are passed by pointers.) Furthermore, `n` can be any type that is convertible to a `Csize_t` integer; the [`ccall`](@ref) implicitly calls [`Base.cconvert(Csize_t, n)`](@ref). @@ -768,13 +778,9 @@ end ``` Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning that the memory -that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should +that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should generally be of type `Ptr{gsl_permutation}`, but it is convertible using [`Base.cconvert`](@ref) and therefore -can be used in the same (covariant) context of the input argument to a [`ccall`](@ref). A pointer -to memory allocated by Julia must be of type `Ref{gsl_permutation}`, to ensure that the memory -address pointed to is valid and that Julia's garbage collector manages the chunk of memory pointed -to correctly. Therefore, the `Ref{gsl_permutation}` declaration allows pointers managed by C or -Julia to be used. +can be used in the same (covariant) context of the input argument to a [`ccall`](@ref). If the C wrapper never expects the user to pass pointers to memory managed by Julia, then using `p::Ptr{gsl_permutation}` for the method signature of the wrapper and similarly in the [`ccall`](@ref) @@ -805,20 +811,11 @@ end ``` The C function wrapped returns an integer error code; the results of the actual evaluation of -the Bessel J function populate the Julia array `result_array`. This variable can only be used -with corresponding input type declaration `Ref{Cdouble}`, since its memory is allocated and managed -by Julia, not C. The implicit call to [`Base.cconvert(Ref{Cdouble}, result_array)`](@ref) unpacks +the Bessel J function populate the Julia array `result_array`. This variable is declared as a +`Ref{Cdouble}`, since its memory is allocated and managed by Julia. The implicit call to +[`Base.cconvert(Ref{Cdouble}, result_array)`](@ref) unpacks the Julia pointer to a Julia array data structure into a form understandable by C. -Note that for this code to work correctly, `result_array` must be declared to be of type `Ref{Cdouble}` -and not `Ptr{Cdouble}`. The memory is managed by Julia and the `Ref` signature alerts Julia's -garbage collector to keep managing the memory for `result_array` while the [`ccall`](@ref) executes. -If `Ptr{Cdouble}` were used instead, the [`ccall`](@ref) may still work, but Julia's garbage -collector would not be aware that the memory declared for `result_array` is being used by the -external C function. As a result, the code may produce a memory leak if `result_array` never gets -freed by the garbage collector, or if the garbage collector prematurely frees `result_array`, -the C function may end up throwing an invalid memory access exception. - ## Fortran Wrapper Example The following example computes a dot product using a Fortran BLAS function. Note the use @@ -1073,20 +1070,3 @@ bindings, see the [CxxWrap](https://github.com/JuliaInterop/CxxWrap.jl) package. [^2]: The [Clang package](https://github.com/ihnorton/Clang.jl) can be used to auto-generate Julia code from a C header file. - -[^3]: For the conversion of ccall parameters, the actual conversion would look something like this: - - ```julia - ccall((:foo, "libfoo"), Cvoid, (Int32, Float64), - Base.unsafe_convert(Int32, Base.cconvert(Int32, x)), - Base.unsafe_convert(Float64, Base.cconvert(Float64, y))) - ``` - - [`Base.cconvert`](@ref) normally just calls [`convert`](@ref), but can be defined to return an - arbitrary new object more appropriate for passing to C. - This should be used to perform all allocations of memory that will be accessed by the C code. - For example, this is used to convert an `Array` of objects (e.g. strings) to an array of pointers. - - [`Base.unsafe_convert`](@ref) handles conversion to [`Ptr`](@ref) types. It is considered unsafe because - converting an object to a native pointer can hide the object from the garbage collector, causing - it to be freed prematurely. From 886d289c34d7feae126ebf53b2f665f10b7f0d20 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 16 Apr 2019 10:01:19 -0700 Subject: [PATCH 3/7] Apply suggestions from code review Co-Authored-By: kmsquire --- doc/src/manual/calling-c-and-fortran-code.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 62d2c0ade2a3e..cc02608785b1e 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -490,7 +490,7 @@ You can get an approximation of a `union` if you know, a priori, the field that the greatest size (potentially including padding). When translating your fields to Julia, declare the Julia field to be only of that type. -Arrays of parameters can be expressed with `NTuple`. For example, this struct in C, +Arrays of parameters can be expressed with `NTuple`. For example, the struct in C notation written as ```c struct B { @@ -724,7 +724,7 @@ by `width[]` and `range[]`; that is, they act like zero-dimensional arrays. ## C Wrapper Examples -Here is a simple example of a C wrapper that returns a `Ptr` type: +Let's start with a simple example of a C wrapper that returns a `Ptr` type: ```julia mutable struct gsl_permutation @@ -778,9 +778,10 @@ end ``` Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning that the memory -that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should generally +that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should be of type `Ptr{gsl_permutation}`, but it is convertible using [`Base.cconvert`](@ref) and therefore -can be used in the same (covariant) context of the input argument to a [`ccall`](@ref). + +Now if you look closely enough at this example, you may notice that it is incorrect, given our explanation above of preferred declaration types. Do you see it? The function we are calling is going to free the memory. This type of operation cannot be given a Julia object (it will crash or cause memory corruption). Therefore, it may be preferable to declare the `p` type as `Ptr{gsl_permutation }`, to make it harder for the user to mistakenly pass another sort of object there than one obtained via `gsl_permutation_alloc`. If the C wrapper never expects the user to pass pointers to memory managed by Julia, then using `p::Ptr{gsl_permutation}` for the method signature of the wrapper and similarly in the [`ccall`](@ref) @@ -818,8 +819,8 @@ the Julia pointer to a Julia array data structure into a form understandable by ## Fortran Wrapper Example -The following example computes a dot product using a Fortran BLAS function. Note the use -of `Ref` and `Ptr`. +The following example utilizes ccall to call a function in a common Fortran library (libBLAS) to computes a dot product. Notice that the argument mapping is a bit different here than above, as we need to map from Julia to Fortran. +On every argument type, we specify `Ref` or `Ptr`. This mangling convention may be specific to your fortran compiler and operating system, and is likely undocumented. However, wrapping each in a `Ref` (or `Ptr`, where equivalent) is a frequent requirement of Fortran compiler implementations: ```julia function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) @@ -1063,10 +1064,8 @@ bindings, see the [CxxWrap](https://github.com/JuliaInterop/CxxWrap.jl) package. [^1]: Non-library function calls in both C and Julia can be inlined and thus may have - even less overhead than calls to shared library functions. When both libraries and executables are - generated by LLVM, it is possible to perform whole-program optimizations that can even optimize - across this boundary, but Julia does not yet support that. In the future, however, it may do so, - yielding even greater performance gains. + even less overhead than calls to shared library functions. + The point above is that the cost of actually doing foreign function call is about the same as doing a call in either native language. [^2]: The [Clang package](https://github.com/ihnorton/Clang.jl) can be used to auto-generate Julia code from a C header file. From a1d7122c33e671d3b29090057e3523331635be13 Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Tue, 16 Apr 2019 11:22:33 -0700 Subject: [PATCH 4/7] Trailing whitespace --- doc/src/manual/calling-c-and-fortran-code.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index cc02608785b1e..72b80cfb1ce51 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -813,7 +813,7 @@ end The C function wrapped returns an integer error code; the results of the actual evaluation of the Bessel J function populate the Julia array `result_array`. This variable is declared as a -`Ref{Cdouble}`, since its memory is allocated and managed by Julia. The implicit call to +`Ref{Cdouble}`, since its memory is allocated and managed by Julia. The implicit call to [`Base.cconvert(Ref{Cdouble}, result_array)`](@ref) unpacks the Julia pointer to a Julia array data structure into a form understandable by C. From eb221b413a81b913054d6a81a633ea3ace2c04da Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Tue, 16 Apr 2019 23:13:49 -0700 Subject: [PATCH 5/7] Apply suggestions from code review Co-Authored-By: kmsquire --- doc/src/manual/calling-c-and-fortran-code.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 72b80cfb1ce51..8e5b15d2442c1 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -56,7 +56,7 @@ to [`ccall`](@ref) are: The `(:function, "library")` pair, return type, and input types must be literal constants (i.e., they can't be variables, but see [Non-constant Function Specifications](@ref) below). - The remaining parameters are evaulated at compile time. + The remaining parameters are evaluated at compile time, when the containing method is defined. !!! note See below for how to [map C types to Julia types](@ref mapping-c-types-to-julia). @@ -779,7 +779,8 @@ end Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning that the memory that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should -be of type `Ptr{gsl_permutation}`, but it is convertible using [`Base.cconvert`](@ref) and therefore +be of type `Ptr{gsl_permutation}`. Because `Ptr` is a subtype of `Ref`, `gsl_permutation_free` will accept +`Ptr` arguments (or any other subtype of `Ref`). Using `Ref` as the argument type additionally makes it possible to pass a handle to memory that has been allocated by Julia. Now if you look closely enough at this example, you may notice that it is incorrect, given our explanation above of preferred declaration types. Do you see it? The function we are calling is going to free the memory. This type of operation cannot be given a Julia object (it will crash or cause memory corruption). Therefore, it may be preferable to declare the `p` type as `Ptr{gsl_permutation }`, to make it harder for the user to mistakenly pass another sort of object there than one obtained via `gsl_permutation_alloc`. From 5c577a23e6501d35597abb071e1bf1d05ba6dbfa Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Tue, 16 Apr 2019 23:16:22 -0700 Subject: [PATCH 6/7] Minor additional cleanup in response to code (well, doc) review --- doc/src/manual/calling-c-and-fortran-code.md | 23 ++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 8e5b15d2442c1..8c46d26765004 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -165,18 +165,20 @@ typedef returntype (*functiontype)(argumenttype, ...) The macro [`@cfunction`](@ref) generates the C-compatible function pointer for a call to a Julia function. The arguments to [`@cfunction`](@ref) are: -1. A Julia Function -2. The return type -3. A literal tuple of input types +1. A Julia function +2. The function's return type +3. A tuple of input types, corresponding to the function signature -As with `ccall`, all of these arguments will be evaluated at compile-time when the containing method is defined. +!!! note + As with `ccall`, the return type and tuple of input types must be literal constants. -Currently, only the platform-default C calling convention is supported. This means that -`@cfunction`-generated pointers cannot be used in calls where WINAPI expects `stdcall` -function on 32-bit windows, but can be used on WIN64 (where `stdcall` is unified with the -C calling convention). +!!! note + Currently, only the platform-default C calling convention is supported. This means that + `@cfunction`-generated pointers cannot be used in calls where WINAPI expects `stdcall` + function on 32-bit windows, but can be used on WIN64 (where `stdcall` is unified with the + C calling convention). -As a classic example, consider the standard C library `qsort` function, declared as: +A classic example is the standard C library `qsort` function, declared as: ```c void qsort(void *base, size_t nmemb, size_t size, @@ -779,8 +781,7 @@ end Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning that the memory that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should -be of type `Ptr{gsl_permutation}`. Because `Ptr` is a subtype of `Ref`, `gsl_permutation_free` will accept -`Ptr` arguments (or any other subtype of `Ref`). Using `Ref` as the argument type additionally makes it possible to pass a handle to memory that has been allocated by Julia. +be of type `Ptr{gsl_permutation}`, but it is convertible using [`Base.cconvert`](@ref) and therefore Now if you look closely enough at this example, you may notice that it is incorrect, given our explanation above of preferred declaration types. Do you see it? The function we are calling is going to free the memory. This type of operation cannot be given a Julia object (it will crash or cause memory corruption). Therefore, it may be preferable to declare the `p` type as `Ptr{gsl_permutation }`, to make it harder for the user to mistakenly pass another sort of object there than one obtained via `gsl_permutation_alloc`. From 697ed8dcbae85976579860cd65ebf4dee40e6758 Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Tue, 16 Apr 2019 23:27:02 -0700 Subject: [PATCH 7/7] Wrap long lines --- doc/src/manual/calling-c-and-fortran-code.md | 55 ++++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 8c46d26765004..a1612e2b3e734 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -421,11 +421,11 @@ checks and is only meant to improve readability of the call. (`void`) but do return, use `Cvoid` instead. !!! note - For `wchar_t*` arguments, the Julia type should be [`Cwstring`](@ref) (if the C routine expects a NUL-terminated - string) or `Ptr{Cwchar_t}` otherwise. Note also that UTF-8 string data in Julia is internally - NUL-terminated, so it can be passed to C functions expecting NUL-terminated data without making - a copy (but using the `Cwstring` type will cause an error to be thrown if the string itself contains - NUL characters). + For `wchar_t*` arguments, the Julia type should be [`Cwstring`](@ref) (if the C routine expects a + NUL-terminated string) or `Ptr{Cwchar_t}` otherwise. Note also that UTF-8 string data in Julia is + internally NUL-terminated, so it can be passed to C functions expecting NUL-terminated data without + making a copy (but using the `Cwstring` type will cause an error to be thrown if the string itself + contains NUL characters). !!! note C functions that take an argument of the type `char**` can be called by using a `Ptr{Ptr{UInt8}}` @@ -512,8 +512,8 @@ end b_a_2 = B.A[3] # note the difference in indexing (1-based in Julia, 0-based in C) ``` -Arrays of unknown size (C99-compliant variable length structs specified by `[]` or `[0]`) are not directly supported. -Often the best way to deal with these is to deal with the byte offsets directly. +Arrays of unknown size (C99-compliant variable length structs specified by `[]` or `[0]`) are not directly +supported. Often the best way to deal with these is to deal with the byte offsets directly. For example, if a C library declared a proper string type and returned a pointer to it: ```c @@ -698,8 +698,8 @@ For translating a C return type to Julia: * If the memory is already owned by Julia, or is an `isbits` type, and is known to be non-null: * `Ref{T}`, where `T` is the Julia type corresponding to `T` - * a return type of `Ref{Any}` is invalid, it should either be `Any` (corresponding to `jl_value_t*`) - or `Ptr{Any}` (corresponding to `jl_value_t**`) + * a return type of `Ref{Any}` is invalid, it should either be `Any` (corresponding to + `jl_value_t*`) or `Ptr{Any}` (corresponding to `jl_value_t**`) * C **MUST NOT** modify the memory returned via `Ref{T}` if `T` is an `isbits` type * If the memory is owned by C: @@ -783,7 +783,11 @@ Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning th that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should be of type `Ptr{gsl_permutation}`, but it is convertible using [`Base.cconvert`](@ref) and therefore -Now if you look closely enough at this example, you may notice that it is incorrect, given our explanation above of preferred declaration types. Do you see it? The function we are calling is going to free the memory. This type of operation cannot be given a Julia object (it will crash or cause memory corruption). Therefore, it may be preferable to declare the `p` type as `Ptr{gsl_permutation }`, to make it harder for the user to mistakenly pass another sort of object there than one obtained via `gsl_permutation_alloc`. +Now if you look closely enough at this example, you may notice that it is incorrect, given our explanation +above of preferred declaration types. Do you see it? The function we are calling is going to free the +memory. This type of operation cannot be given a Julia object (it will crash or cause memory corruption). +Therefore, it may be preferable to declare the `p` type as `Ptr{gsl_permutation }`, to make it harder for the +user to mistakenly pass another sort of object there than one obtained via `gsl_permutation_alloc`. If the C wrapper never expects the user to pass pointers to memory managed by Julia, then using `p::Ptr{gsl_permutation}` for the method signature of the wrapper and similarly in the [`ccall`](@ref) @@ -821,8 +825,12 @@ the Julia pointer to a Julia array data structure into a form understandable by ## Fortran Wrapper Example -The following example utilizes ccall to call a function in a common Fortran library (libBLAS) to computes a dot product. Notice that the argument mapping is a bit different here than above, as we need to map from Julia to Fortran. -On every argument type, we specify `Ref` or `Ptr`. This mangling convention may be specific to your fortran compiler and operating system, and is likely undocumented. However, wrapping each in a `Ref` (or `Ptr`, where equivalent) is a frequent requirement of Fortran compiler implementations: +The following example utilizes ccall to call a function in a common Fortran library (libBLAS) to +computes a dot product. Notice that the argument mapping is a bit different here than above, as +we need to map from Julia to Fortran. On every argument type, we specify `Ref` or `Ptr`. This +mangling convention may be specific to your fortran compiler and operating system, and is likely +undocumented. However, wrapping each in a `Ref` (or `Ptr`, where equivalent) is a frequent +requirement of Fortran compiler implementations: ```julia function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) @@ -851,8 +859,9 @@ the C library notifies you that it is finished with them. Whenever you have created a pointer to Julia data, you must ensure the original data exists until you are done with using the pointer. Many methods in Julia such as [`unsafe_load`](@ref) and [`String`](@ref) make copies of data instead of taking ownership of the buffer, so that it is -safe to free (or alter) the original data without affecting Julia. A notable exception is [`unsafe_wrap`](@ref) -which, for performance reasons, shares (or can be told to take ownership of) the underlying buffer. +safe to free (or alter) the original data without affecting Julia. A notable exception is +[`unsafe_wrap`](@ref) which, for performance reasons, shares (or can be told to take ownership of) the +underlying buffer. The garbage collector does not guarantee any order of finalization. That is, if `a` contained a reference to `b` and both `a` and `b` are due for garbage collection, there is no guarantee @@ -875,8 +884,8 @@ with `$`). For this reason, `eval` is typically only used to form top-level defi when wrapping libraries that contain many similar functions. A similar example can be constructed for [`@cfunction`](@ref). -However, doing this will also be very slow and leak memory, so you should usually avoid this and instead keep reading. -The next section discusses how to use indirect calls to efficiently accomplish a similar effect. +However, doing this will also be very slow and leak memory, so you should usually avoid this and instead keep +reading. The next section discusses how to use indirect calls to efficiently accomplish a similar effect. ## Indirect Calls @@ -945,8 +954,8 @@ and load in the new changes. One can either restart Julia or use the ```julia lib = Libdl.dlopen("./my_lib.so") # Open the library explicitly. sym = Libdl.dlsym(lib, :my_fcn) # Get a symbol for the function to call. -ccall(sym, ...) # Use the pointer `sym` instead of the (symbol, library) tuple (remaining arguments are the same). -Libdl.dlclose(lib) # Close the library explicitly. +ccall(sym, ...) # Use the pointer `sym` instead of the (symbol, library) tuple (remaining arguments are the +same). Libdl.dlclose(lib) # Close the library explicitly. ``` Note that when using `ccall` with the tuple input @@ -957,8 +966,8 @@ and it may not be explicitly closed. The second argument to [`ccall`](@ref) can optionally be a calling convention specifier (immediately preceding return type). Without any specifier, the platform-default C calling convention is used. -Other supported conventions are: `stdcall`, `cdecl`, `fastcall`, and `thiscall` (no-op on 64-bit Windows). For example (from -`base/libc.jl`) we see the same `gethostname`[`ccall`](@ref) as above, but with the correct +Other supported conventions are: `stdcall`, `cdecl`, `fastcall`, and `thiscall` (no-op on 64-bit Windows). +For example (from `base/libc.jl`) we see the same `gethostname`[`ccall`](@ref) as above, but with the correct signature for Windows: ```julia @@ -1019,12 +1028,14 @@ back to a Julia object reference by [`unsafe_pointer_to_objref(ptr)`](@ref). (Ju can be converted to `jl_value_t*` pointers, as `Ptr{Cvoid}`, by calling [`pointer_from_objref(v)`](@ref).) The reverse operation (writing data to a `Ptr{T}`), can be performed using [`unsafe_store!(ptr, value, [index])`](@ref). -Currently, this is only supported for primitive types or other pointer-free (`isbits`) immutable struct types. +Currently, this is only supported for primitive types or other pointer-free (`isbits`) immutable struct +types. Any operation that throws an error is probably currently unimplemented and should be posted as a bug so that it can be resolved. -If the pointer of interest is a plain-data array (primitive type or immutable struct), the function [`unsafe_wrap(Array, ptr,dims, own = false)`](@ref) +If the pointer of interest is a plain-data array (primitive type or immutable struct), the function +[`unsafe_wrap(Array, ptr,dims, own = false)`](@ref) may be more useful. The final parameter should be true if Julia should "take ownership" of the underlying buffer and call `free(ptr)` when the returned `Array` object is finalized. If the `own` parameter is omitted or false, the caller must ensure the buffer remains in existence until