Skip to content
AgileLogics
6 min read

Calling C and Python

Prerequisites: Type stability

Julia can call C (and Fortran) libraries directly, with no wrapper code and no overhead beyond a normal C function call.

ccall and @ccall

n = ccall(:strlen, Csize_t, (Cstring,), "hello")
println(n)

m = @ccall strlen("hello, world"::Cstring)::Csize_t
println(m, " ", typeof(m))

println(@ccall abs(-42::Cint)::Cint)

exponent = Ref{Cint}(0)
mantissa = @ccall frexp(40.0::Cdouble, exponent::Ptr{Cint})::Cdouble
println(mantissa, " * 2^", exponent[])
5
12 UInt64
42
0.625 * 2^6
  • ccall(name, ReturnType, (ArgTypes...,), args...) or the more readable @ccall name(arg::Type, ...)::ReturnType.
  • C types have Julia names: Cint, Cdouble, Csize_t, Cstring, Ptr{T}.
  • A Ref passes a pointer to a value the C function can write to — frexp stores the exponent there.

Your own C library

// illustrative — mathlib.c, compiled with:
//   gcc -shared -fPIC -O2 -o libmathlib.so mathlib.c   (Linux; .dll on Windows, .dylib on macOS)
double dot(const double *a, const double *b, int n) {
    double s = 0;
    for (int i = 0; i < n; i++) s += a[i] * b[i];
    return s;
}
# illustrative — needs the compiled library next to the script
const LIB = joinpath(@__DIR__, "libmathlib")

cdot(a::Vector{Float64}, b::Vector{Float64}) =
    @ccall LIB.dot(a::Ptr{Cdouble}, b::Ptr{Cdouble}, length(a)::Cint)::Cdouble

println(cdot([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]))
32.0

Julia arrays of Float64 are laid out like C arrays, so they're passed as pointers without copying. Packages ship compiled libraries as JLL packages (built with BinaryBuilder.jl), so users don't need a compiler. Learn more about C itself in the C course.

Python with PythonCall.jl

PythonCall.jl lets Julia use any Python package (and juliacall lets Python call Julia). It manages its own Python environment with CondaPkg.jl:

# illustrative — needs PythonCall and the numpy Python package
using PythonCall

np = pyimport("numpy")
a = np.array([1.0, 2.0, 3.0])
println(np.mean(a))
total = pyconvert(Float64, np.sum(a))
println(total + 1)

Python objects appear in Julia as Py values; pyconvert(T, x) converts them to Julia types. (The older PyCall.jl package does the same job with a different API.) This is handy for libraries that exist only in Python — see the Python course. RCall.jl does the same for R.