While Julia can rightfully claim to obviate the need to write some C or Fortran code, it is possible that you will need to interact with the existing C or Fortran shared libraries. Functions in such a library can be called directly by Julia, with no glue code, boilerplate code, or compilation needed. Because Julia's LLVM compiler generates native code, calling a C function from Julia has exactly the same overhead as calling the same function from C code itself. However, first, we need to know a few more things:
- For calling out to C, we need to work with pointer types; a native pointer Ptr{T} is nothing more than the memory address for a variable of type T. You can use Cstring if the value is null-terminated.
- At this lower level, the term primitive is also used. primitive is a concrete type whose data consists of bits, such as Int8, UInt8, Int32, Float64, Bool, and Char.
- To pass a string to C, it is converted to a contiguous byte array representation with the function unsafe_string(); given Ptr to a C string, it returns a Julia string.
Here is how to call a C function in a shared library (calling Fortran is done similarly). Suppose we want to know the value of an environment variable in our system, say, the language; we can obtain this by calling the C function getenv from the shared library libc:
# code in Chapter 9callc.jl: lang = ccall( (:getenv, "libc"), Cstring, (Cstring,), "LANG")
This returns a Cstring. To see its string contents, execute unsafe_string(lang), which returns en_US.
In general, ccall takes the following arguments:
- A (:function, "library") tuple, where the name of the C function (here, getenv) is used as a symbol, and the library name (here, libc) as a string
- The return type (here, Cstring), which can also be any primitive, or Ptr
- A Cstring as input arguments: note the tuple notation (Cstring,)
- The actual arguments, if there are any (here, "LANG")
It is generally advisable to test for the existence of a library before doing the call. This can be tested like this: find_library(["libc"]), which returns "libc" when the library is found, or " " when it cannot find the library.
When calling a Fortran function, all inputs must be passed by reference. Arguments to C functions are, in general, automatically converted, and the returned values in C types are also converted to Julia types. Arrays of Booleans are handled differently in C and Julia and cannot be passed directly, so they must be manually converted. The same applies for some system-dependent types.
The ccall function will also automatically ensure that all of its arguments will be preserved from garbage collection until the call returns. C types are mapped to Julia types. For example, short is mapped to Int16, and double to Float64.
A complete table of these mappings, as well as a lot more intricate details, can be found in the Julia docs at http://docs.julialang.org/en/latest/manual/calling-c-and-fortran-code/. The other way around is also possible, by calling Julia functions from C code (or embedding Julia in C); refer to http://docs.julialang.org/en/latest/manual/embedding/. Julia and C can also share array data without copying.
If you have the existing C code, you must compile it as a shared library to call it from Julia. With GCC, you can do this using the -shared -fPIC command-line arguments. Support for C++ is more limited and is provided by the Cpp and Clang packages.