Skip to content

Commit

Permalink
Document some examples of C wrappers using ccall
Browse files Browse the repository at this point in the history
- Remove references to #2818

- Explain when to use `Ptr{T}` and when to use `Ref{T}` correctly.
  `Ptr` is generally used for return types and fields of types mirroring
  C structs. `Ref` is generally used for input types, allowing memory
  managed by either C or Julia to be referenced by `ccall`.

- Provide some examples of C wrappers simplified from GSL.jl

- Other minor formatting change

Ref: JuliaMath/GSL.jl#43
  • Loading branch information
jiahao committed Feb 4, 2016
1 parent f020d38 commit 115cbd0
Showing 1 changed file with 83 additions and 20 deletions.
103 changes: 83 additions & 20 deletions doc/manual/calling-c-and-fortran-code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ All other types are stored as a pointer to the data.
When mirroring a struct used by-value inside another struct in C,
it is imperative that you do not attempt to manually copy the fields over,
as this will not preserve the correct field alignment.
Instead, declare an immutable isbits type and use that instead.
Instead, declare an immutable ``isbits`` type and use that instead.
Unnamed structs are not possible in the translation to Julia.

Packed structs and union declarations are not supported by Julia.
Expand All @@ -526,7 +526,7 @@ When translating your fields to Julia, declare the Julia field to be only
of that type.

Arrays of parameters must be expanded manually, currently
(either inline, or in an immutable helper-type). For example::
(either inline, or in an immutable helper type). For example::

in C:
struct B {
Expand All @@ -549,8 +549,8 @@ Arrays of unknown size are not supported.

In the future, some of these restrictions may be reduced or eliminated.

Memory Ownership:
~~~~~~~~~~~~~~~~~
Memory Ownership
~~~~~~~~~~~~~~~~

**malloc/free**

Expand All @@ -562,29 +562,21 @@ in the ``free`` function being called via the wrong ``libc`` library and
cause Julia to crash. The reverse (passing an object allocated in Julia
to be freed by an external library) is equally invalid.

**Ptr{T} vs. Array{T} vs. Ref{T} vs. T**

The choice of type-wrapper declaration strongly depends on who allocated the memory,
and the declared type.
In general, use ``T`` if the memory is intended to be allocated in
(and managed by) Julia (with type-tag).
Use ``Ptr{T}`` if the memory is expected to be populated by ``C`` (without type-tag).
Use ``Ref{T}`` if you have an ``isbits`` type,
but you want to turn it into a pointer to a struct in another struct definition.
When to use T, Ptr{T} and Ref{T}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

See issue #2818 for some work that needs to be done to simplify this so that Julia
types can be used to recursively mirror c-style structs,
without requiring as much manual management of the ``Ptr`` conversions.
After #2818 is implemented, it will be true that an ``Vector{T}`` will be equivalent to
an ``Ptr{Ptr{T}}``. That is currently not true, and the conversion must be explicitly.
In C wrapper code that must handle pointers, ``Ref{T}`` should generally be
used for the types of input arguments, whereas ``Ptr{T}`` should generally be
used for the output type, and also within Julia immutable types designed to
mimic the internal structure of corresponding C structs.

Mapping C Functions to Julia
----------------------------

ccall/cfunction argument translation guide
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For translating a ``c`` argument list to ``Julia``:
For translating a ``C`` argument list to ``Julia``:

* ``T``, where ``T`` is one of the primitive types:
``char``, ``int``, ``long``, ``short``, ``float``, ``double``, ``complex``, ``enum``
Expand Down Expand Up @@ -638,7 +630,7 @@ For translating a ``c`` argument list to ``Julia``:
ccall/cfunction return type translation guide
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For translating a ``c`` return type to ``Julia``:
For translating a ``C`` return type to ``Julia``:

* ``void``

Expand Down Expand Up @@ -743,6 +735,77 @@ 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
---------------------------

Here is a simple example of a C wrapper that returns a ``Ptr`` type::

type gsl_permutation
end

# The corresponding C signature is
# gsl_permutation * gsl_permutation_alloc (size_t n);
function permutation_alloc(n::Integer)
output_ptr = ccall( (:gsl_permutation_alloc, :libgsl),
Ptr{gsl_permutation}, (Csize_t,), n )
if output_ptr==C_NULL #Could not allocate memory
throw(MemoryError())
end
return output_ptr
end

The GSL library (here assumed to be accessible through ``:libgsl``) defines an
opaque pointer, ``gsl_permutation *``, as the return type of the C function
``gsl_permutation_alloc()``. As user code never has to look inside the
``gsl_permutation`` 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`` is declared as ``Ptr{gsl_permutation}``, since
the memory allocated and pointed to by ``output_ptr`` is controlled by C (and
not Julia).

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.
Furthermore, ``n`` can be any type that is convertable to a ``Csize_t``
integer; the ``ccall`` implicitly calls ``convert(Csize_t, n)``.


Here is a second example wrapping the corresponding destructor::

# The corresponding C signature is
# void gsl_permutation_free (gsl_permutation * p);
function permutation_free(p::Ref{gsl_permutation})
ccall( (:gsl_permutation_free, :libgsl), Void,
(Ref{gsl_permutation},), p )
end

Here, the input ``p`` is declared to be of type ``Ref{gsl_permutation}``.
``p`` may be of type ``Ptr{gsl_permutation}``, as may be returned by
``permutation_alloc()``, but in principle could also be constructed from memory
managed by Julia.


Here is a third example passing Julia arrays::

# The corresponding C signature is
# int gsl_sf_bessel_Jn_array (int nmin, int nmax, double x,
# double result_array[])
function sf_bessel_Jn_array(nmin::Integer, nmax::Integer, x::Real)
if nmax<nmin throw(DomainError()) end
result_array = Array(Cdouble, nmax-nmin+1)
errno = ccall( (:gsl_sf_bessel_Jn_array, :libgsl), Cint, (Cint, Cint,
Cdouble, Ref{Cdouble}), nmin, nmax, x, result_array )
if errno!= 0 error("GSL error code $errno") end
return result_array
end

This function returns an integer error code; the results of the actual
evaluation of the Bessel J-function is populated in an array. A Julia array can
be used with corresponding input type declaration ``Ref{Cdouble}``, since
``result_array`` is a Julia array whose memory is allocated and managed by
Julia, not C.



Garbage Collection Safety
-------------------------
Expand Down

0 comments on commit 115cbd0

Please sign in to comment.