Permalink
Switch branches/tags
KristofferC-patch-1 KristofferC-patch-2 KristofferC-patch-3 KristofferC-patch-4 KristofferC-patch-5 KristofferC-patch-6 KristofferC-patch-7 aa/aggressive-remove_linenums aa/extrema-function aa/glibc aa/join-function aa/peek-a-boo aa/1.1.0-rc1 aa/1.2.0-DEV adr/snapshot ajf/broadcast-dicts ajf/cartesianrange-array ajf/deprecatemultivaluenonscalarindexedassignment ajf/dict-iterate-values ajf/hasindex ajf/mappedarray ajf/no-pure-adjoint ajf/only ajf/pairs2 ajf/propage_inbounds_broadcast ajf/redundant-dict-code ajf/sets ajf/tuplekeys ajf/vectortranspose2 ajf/view-of-range amitm/cachingpool amitm/parfor2 amitm/threading amp-ref an/I an/In anj/gees anj/genlu anj/map anj/nosuitesparse anj/novals anj/smallmul anj/static backport-1.0.3 backport-1.1.0 breakmore cjf/fpe-exceptions cjf/line-continuation cjf/runtime-logging cjf/show-exception-stacks cjh/doxygen cjh/fix-4774 cjh/hess cjh/parallel-bench cjh/perftip-fixup cv/bigfloat-trunc cv/dates-replace cv/invalid-rational cv/libgit2-experiment cv/pkg-test-evalfile fe/default-environment fe/hermsym-matrixfunc fe/integer-construction fe/loggerstream fe/numbered-docs fe/structured-matrix-constructor fe/trueorfalse fix9475 gpu-broadcast id_norm ihn/fix12806 ihnorton-patch-fnlz-doc ird/substrcon jb/anyeltype jb/arraygcperf jb/braceshcat jb/cgroots jb/codeinfostuff jb/convertmissingnothing jb/depyieldto jb/dotcolon jb/fastercat jb/fix14106 jb/fix29059 jb/fix29501 jb/fix29594 jb/hash0 jb/implicitssa jb/interpreter_opt jb/interpretmore jb/jr/tuplelattice jb/latency3 jb/latency10 jb/lessinferenceinfo jb/lexproduct jb/modprefixshow jb/ordereddict jb/precompile_check jb/r5rs jb/rethrow jb/serializeperf jb/typemap jb/typemap0 jb/warnmissingcode jb/zeroarrays jb/06_compiler_perf jcb/arrayviews jcb/debugdocs jcb/evalmacrocallthrows jcb/genfunreflect jcb/juliarepl jcb/pkg_casesensitive jcb/pkgerror jcb/randchar jcb/rm04deps jcb/simdmdloop jh/float-colon jh/keys-and-vals jh/setops jmm/repl_hooks jmm/select jmm/string_rename jmw/null-syntax jn/Fusion jn/StructRet_abi jn/apply_type_error_message jn/backedge-fixup jn/cg-class jn/channel-mt jn/check_sadd jn/codegen-norecursion jn/codegen_rewrite jn/compile-all-0.6 jn/compile_all_generic_box jn/condition-mt jn/drop-inexact jn/eschnett/clang-format jn/fix-macro-scope-design jn/fixup-pty-and-precompile jn/frontend-cl-closure-perf jn/gc-pool-lazy jn/gc_stack jn/generalized-LOAD_PATH-compilecache jn/iddict jn/in-isequal jn/infer-Const-cache jn/iocolor jn/iocolor2 jn/iterate jn/jb-subtype-const-type-fix jn/jb-subtype-faster-isa-Type jn/jb-subtype jn/jl_bitszero jn/jlinterpreter jn/kf-linecolstr jn/kf/iterate-wip jn/kf/newlocs jn/kwcall jn/lazydoc jn/less-at-generated jn/license-committers jn/llvmcall_jl_value_t jn/llvmcall2 jn/loading-plus jn/loading-refactoring-docs jn/loading-refactoring jn/localize-multiversioning jn/lower-inline_cost_threshold jn/ml-match-opt jn/modulecoalescing jn/msvclink jn/newir-test-fixes jn/opt-makefile-compile-take2 jn/release-0.5-faster-front jn/release0.6-26833 jn/simple-scope jn/slot-type-infer-optimize jn/stagedfail jn/steprange jn/subtype-sig-equal jn/test-warn-repr jn/topmodule jn/travis-test jn/tune_whos jn/tuplecc jn/type-construction-normalization jn/typemap-name jn/typename_hide_null_svec jn/unhandled_exception_handler jn/union-bits-layout jn/update-precompile-heuristic jn/warn_dllist jn/widen_method_matchs jn/win32_debug_julia_got jn/26512 jn/29872-compare jq/array-union-bits2 jq/buffer jq/mmap2 jq/mmqp jq/namedtuplepatch jq/namedtuples jq/parsing jq/union-bits-layout-rebased jr/betterspoof jr/cinfoworldbounds jr/cinfoworldboundstmp jr/compiledebug jr/doasyouaretold jr/ptupleprop jr/testlog jr/tmp2 jr/tupleconst jr/tuplekindtypefix jr/tuplelatticeold jr/wip kc/JULIA_ENV kc/TM kc/anonymous kc/break_linalg kc/cache_available kc/callbacks kc/check_overwritten kc/code_loading_hook kc/codepoint_to_utf8 kc/copyto_inline kc/debug_partr_precompile kc/debug_precompile kc/debug kc/doctest_meta2 kc/doctests_CI kc/dom_err_msgs kc/edit_symbol kc/err_msg_invalid_help_query kc/exception_trait kc/excise_libgit2_v2 kc/fix_doctests kc/fix_init kc/fix_loading_top_level kc/ggwp kc/gimp_precompile kc/ident kc/immut_dict kc/index_sparse_column kc/inference_regression kc/inline_print kc/loading_debug_error kc/mod_bind_name kc/move_dates_anub kc/no_top_level_scope_frame_repl kc/ohnohedidn kc/online_with_the_rest kc/outline_log kc/parse_simple kc/perf_ident kc/pkg3_documenter kc/pkg3_patch kc/pkg3 kc/pkgstdlib kc/precomp_pkg3 kc/precompile_overhead kc/precompile_with_pkg3 kc/relax_strided_sparse_matmul kc/repl_fast kc/robustness kc/search_str_perf kc/seed_fast kc/set_threads kc/simpler_copyto kc/so_long_and_thanks_for_the_packages kc/sparse_check kc/split_testhelpers kc/stdlib_loadpath_buildtest kc/stdlib_markdown kc/stdlib_printing kc/stdlib kc/test_inline kc/test_linfo kc/test kc/trim_load_path_on_sysimg_load kc/whatdocstoshow kc/workflow kf/abstractstringit kf/aliasscopes kf/_apply_apply_type kf/assertegal kf/bigsroa kf/cfgsimp kf/cfgwip kf/clangsa kf/clangwinround kf/cmdlineargstimeout kf/codegen kf/codetypedconst kf/dsymutil kf/effectfree2 kf/eps kf/filestdio kf/gallium kf/gcrootdeleter kf/gotoifnottrap kf/hashpreserve kf/inferencecomplexity kf/iter kf/iteratewip kf/jb/rm_deps_3 kf/jn/26512 kf/latesplitting kf/limitdoc kf/llvm38cmake kf/llvmpasstests kf/magiccompilerdustforceleste2 kf/magiccompilerdustforceleste3 kf/magiccompilerdustforceleste4 kf/mimedata kf/mmap kf/msys2cmd kf/multiany kf/mutatingimmutables kf/newdinfo kf/newinline kf/newoptselectvalue kf/permuteddimssimilar kf/piperedirect kf/powovf kf/pzip kf/refactortodo kf/reinterpretredux kf/replfixes kf/sanitizerbuild kf/sanitizermake kf/segvjl_ kf/stdioredirect kf/symimmut kf/tbaaipo kf/topush kf/tpu kf/tpu2 kf/tpu3 kf/uncolon kf/unionpenalties kf/unionsplit2 kf/unwindagain kf/uvint32 kf/wasm kf/winfix kf/wip kf/xlademo kf/11902 kms/c_fortran_doc_updates kms/find_extrema kp/old_partr kp/partr kp/schedtask ksh/basicstring ksh/concatex ksh/doccandp ksh/docmacros ksh/errshow ksh/linalgtests ksh/lineup ksh/manmod ksh/nt ksh/nwaiter ksh/pinvtri ksh/pkgtestset ksh/svds ksh/uschol master mb/NEWSupdate mb/broadcasterable mb/cartesiansubarray mb/consttypefields mb/deprecatemultivaluenonscalarindexedassignment mb/doctake! mb/extensible-method-error mb/inbounds-expression mb/indexed-eltype mb/intsetrefactor mb/native-bounds mb/recursive-cat mb/reductioneeze mb/remotehashall mb/reverseunsignedrange mb/sparsefinders mb/stridedtransposes mb/thisshouldhaveoccurredtome mb/transpose mb/unaliascopy-reinterprets mb/value-based-BroadcastStyle mb/viewdefault mb/whisperdownthelane mbauman-patch-1 mh/divrem_u128_i686 mh/fix_25430 mh/patch_rdims mh/specialize_similar_for mh/tmerge_to_union_more_often mji/zygote mn/3150 mn/5333 mn/8828 moon/hygienic-macros nl/cat nl/firstlast nl/fpsort nl/is nl/length nl/mapreducedim nl/mbedtls nl/repeated nl/setindex! nl/sleep nl/sort nl/strmissing ob/aepsf ob/cov ob/cttx ob/fixdispatch ob/fty ob/goodbye-type_goto ob/ibit ob/infsplat ob/interp_call ob/mapfl ob/mmapquota ob/ptrfree ob/rref ob/stackmaps ob/stknew ob/stkw ob/stp release-0.1 release-0.2 release-0.3 release-0.4 release-0.5 release-0.6 release-0.7 release-1.0 release-1.1 revert-22049-kf/memcpyopt revert-26854-kc/bump_pkg3_6 revert-28319-kc/precompiler_build2 revert-29494-kf/uncolon_recursion rf/Bit rf/MT-show rf/autoindentpaste rf/big-init2 rf/bigint-native rf/count-etc rf/deepcopy rf/defaults rf/digits-unsigned-nonrecur rf/digits-unsigned rf/doc-push rf/doc-remove-stacktraces rf/hex2num-deprecate rf/iter-access rf/ndigits-bases rf/pure-push rf/rand-distrib rf/rand-f16-shift rf/rand/RANDOM_DEVICE rf/rand/fast rf/rand/frompair rf/rand/genpair rf/rand/ndl rf/rand/range-bool rf/rand/unleash rf/randn-fillarray rf/regexp-concat rf/repl/iocontext rf/repl/simplify rf/set-ctor rf/str-pred-case rf/switch-GLOBAL_RNG rf/union-fix-max_values rf/union-immutable-set rf/unmerge-merge rf/0x-BigInt sb/abstractcomplex sb/accumulate sb/callsuper sb/div12 sb/dllist sb/dmg-bg sb/doc-select sb/enumset sb/exit sb/fastmath sb/firstday sb/foldr sb/irrational-bigfloat sb/libgit2/peel sb/pipelineerror sb/qr2 sb/rearrange sb/reduce-and sb/reduce-empty sb/reduce sb/roundbool sb/suitesparse-news sb/triangular sc_mtime sf/JULIA_CPU_TARGET sf/background_output_prepending sf/gfortran4 sf/http_parser sf/httpbin sf/matrix_conversions sf/memprofiler sf/openblas_no_patches sf/remove_build_sysimg sf/sticky_shells_and_ringing_bells sf/suitesparse_ldflags sf/testing sf/travisosx sf/which_doesnt_throw sjk/int-print sjk/spqr sk/bytes sk/chop sk/copynothing sk/depsload sk/highlander.alt sk/highlander.alt1 sk/highlander.alt2 sk/libgit2-discover sk/linspace sk/lockfile sk/markdown sk/median-fix sk/my-precious sk/newchar sk/next-to-nothing sk/pkg3up sk/pkg3 sk/precompile-blacklist sk/reqfrom sk/str sk/strdiv sk/stringstate sk/trig sk/void sk/winpaths sk/x-init sk/xchar srp/repl-undo staging stevengj-patch-1 stevengj-patch-2 tan/readdlm2 tb/codegen-norecursion tb/cuda tb/ext_langimpl tb/fnattrs tb/hotfix tb/inference_hooks tb/log_nothing tb/loglevels tb/pmb_test tb/poison tb/strip_as teh/better_errors2 teh/c_callgraph teh/depbind_module_rebased teh/dict_empty teh/disable_inline_macros teh/ducktype_functions teh/find_cleanup teh/findfirst_next teh/index_with_numbers teh/inlining_unreachable teh/linspace4 teh/method_exists teh/printf teh/reshapedarrays teh/shareindexes teh/splatting_numbers_inference teh/sub_inference teh/tuple_limit teh/tupleutils teh/type_redefinition teh/unitstrides teh/valen teh/valen3 teh/workermodules test_testset_overhead tk/checkdeps tk/fix20925 tk/geev tk/pkgsubtract tk/shell-quotes-windows tk/sparsediv0 vc/aligned_bitstype vc/asan_tasks vc/compiler-rt vc/distloading vc/float16_intrinsics vc/float16 vc/float128_basic vc/float128 vc/int128_atomics vc/irshow vc/jlrt vc/llvm_undef vc/no_copy_stacks vc/pool_alignment vc/power_openblas vc/ppc-llvm6 vc/ppc_unix_signals vc/restored-1.0.1 vc/revert28449 vc/smallints vc/tapir vc/tsan_nort vc/tsan vc/vsts vc/win_poolalignment vc/x86low vc/7prep vs/arm-delete-readme vs/macports yyc/arm/crc32c yyc/arm/fenv-0.5 yyc/banner yyc/bigint yyc/build/doc yyc/build/llvm yyc/call-site-cache yyc/cmake yyc/codegen/alloc2 yyc/codegen/eh yyc/codegen/local-push yyc/codegen/wb2 yyc/const-jlcall yyc/dft_split yyc/dftnew_rebase yyc/fieldcall-0.7 yyc/fieldcall yyc/gc-alloc yyc/gc-debug-env yyc/gc/safepoint-rt yyc/gc/thread-DO-NOT-merge yyc/less-getfield yyc/ovldot yyc/show-new yyc/test/release-0.6 yyc/tests/appveyor yyc/tests/arm yyc/tests/llvm39 yyc/tests/master yyc/travis-test yyc/typeinf/pure yyc/undef_sig
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1082 lines (836 sloc) 56.1 KB

Calling C and Fortran Code

Though most code can be written in Julia, there are many high-quality, mature libraries for numerical computing already written in C and Fortran. To allow easy use of this existing code, Julia makes it simple and efficient to call C and Fortran functions. Julia has a "no boilerplate" philosophy: functions can be called directly from Julia without any "glue" code, code generation, or compilation -- even from the interactive prompt. This is accomplished just by making an appropriate call with ccall syntax, which looks like an ordinary function call.

The code to be called must be available as a shared library. Most C and Fortran libraries ship 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.)

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.

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 functions, functions in the Julia runtime, or functions in an application linked to Julia.

By default, Fortran compilers generate mangled names (for example, converting function names to lowercase or uppercase, often appending an underscore), and so to call a Fortran function via ccall you must pass the mangled identifier corresponding to the rule followed by your Fortran compiler. Also, when calling a Fortran function, all inputs must be passed as pointers to allocated values on the heap or stack. This applies not only to 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 to actually generate a call to the library function. Arguments to ccall are as follows:

  1. A (:function, "library") pair, which must be written as a literal constant,

    OR

    a :function name symbol or "function" name string, which is resolved in the current process,

    OR

    a function pointer (for example, from dlsym).

  2. Return type (see below for mapping the declared C type to Julia)

    • This argument will be evaluated at compile-time, when the containing method is defined.
  3. A tuple of input types. The input types must be written as a literal tuple, not a tuple-valued variable or expression.

    • This argument will be evaluated at compile-time, when the containing method is defined.
  4. The following arguments, if any, are the actual argument values passed to the function.

As a complete but simple example, the following calls the clock function from the standard C library:

julia> t = ccall((:clock, "libc"), Int32, ())
2292761

julia> t
2292761

julia> typeof(ans)
Int32

clock takes no arguments and returns an Int32. One common gotcha is that a 1-tuple must be written with a trailing comma. For example, to call the getenv function to get a pointer to the value of an environment variable, one makes a call like this:

julia> path = ccall((:getenv, "libc"), Cstring, (Cstring,), "SHELL")
Cstring(@0x00007fff5fbffc45)

julia> unsafe_string(path)
"/bin/bash"

Note that the argument type tuple must be written as (Cstring,), rather than (Cstring). This is because (Cstring) is just the expression Cstring surrounded by parentheses, rather than a 1-tuple containing Cstring:

julia> (Cstring)
Cstring

julia> (Cstring,)
(Cstring,)

In practice, especially when providing reusable functionality, one generally wraps ccall uses in Julia functions that set up arguments and then check for errors in whatever manner the C or Fortran function indicates them, propagating to the Julia caller as exceptions. This is especially important since C and Fortran APIs are notoriously inconsistent about how they indicate error conditions. For example, the getenv C library function is wrapped in the following Julia function, which is a simplified version of the actual definition from env.jl:

function getenv(var::AbstractString)
    val = ccall((:getenv, "libc"),
                Cstring, (Cstring,), var)
    if val == C_NULL
        error("getenv: undefined variable: ", var)
    end
    unsafe_string(val)
end

The C getenv function indicates an error by returning NULL, but other standard C functions indicate errors in various different ways, including by returning -1, 0, 1 and other special values. This wrapper throws an exception clearly indicating the problem if the caller tries to get a non-existent environment variable:

julia> getenv("SHELL")
"/bin/bash"

julia> getenv("FOOBAR")
getenv: undefined variable: FOOBAR

Here is a slightly more complex example that discovers the local machine's hostname:

function gethostname()
    hostname = Vector{UInt8}(undef, 128)
    ccall((:gethostname, "libc"), Int32,
          (Ptr{UInt8}, Csize_t),
          hostname, sizeof(hostname))
    hostname[end] = 0; # ensure null-termination
    return unsafe_string(pointer(hostname))
end

This example first allocates an array of bytes, then calls the C library function gethostname to fill the array in with the hostname, takes a pointer to the hostname buffer, and converts the pointer to a Julia string, assuming that it is a NUL-terminated C string. It is common for C libraries to use this pattern of requiring the caller to allocate memory to be passed to the callee and filled in. Allocation of memory from Julia like this is generally accomplished by creating an uninitialized array and passing a pointer to its data to the C function. This is why we don't use the Cstring type here: as the array is uninitialized, it could contain NUL bytes. Converting to a Cstring as part of the ccall checks for contained NUL bytes and could therefore throw a conversion error.

Creating C-Compatible Julia Function Pointers

It is possible to pass Julia functions to native C functions that accept function pointer arguments. For example, to match C prototypes of the form:

typedef returntype (*functiontype)(argumenttype, ...)

The macro @cfunction generates the C-compatible function pointer for a call to a Julia function. Arguments to @cfunction are as follows:

  1. A Julia Function
  2. 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.

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:

void qsort(void *base, size_t nmemb, size_t size,
           int (*compare)(const void*, const void*));

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 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 <):

julia> function mycompare(a, b)::Cint
           return (a < b) ? -1 : ((a > b) ? +1 : 0)
       end
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.

In order to pass this function to C, we obtain its address using the macro @cfunction:

julia> mycompare_c = @cfunction(mycompare, Cint, (Ref{Cdouble}, Ref{Cdouble}));

@cfunction requires three arguments: the Julia function (mycompare), the return type (Cint), and a literal tuple of the input argument types, in this case to sort an array of Cdouble (Float64) elements.

The final call to qsort looks like this:

julia> A = [1.3, -2.7, 4.4, 3.1]
4-element Array{Float64,1}:
  1.3
 -2.7
  4.4
  3.1

julia> ccall(:qsort, Cvoid, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Cvoid}),
             A, length(A), sizeof(eltype(A)), mycompare_c)

julia> A
4-element Array{Float64,1}:
 -2.7
  1.3
  3.1
  4.4

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).

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 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 can be used to auto-generate Julia code from a C header file.)

Auto-conversion:

Julia automatically inserts calls to the Base.cconvert function to convert each argument 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:

ccall((:foo, "libfoo"), Cvoid, (Int32, Float64),
      Base.unsafe_convert(Int32, Base.cconvert(Int32, x)),
      Base.unsafe_convert(Float64, Base.cconvert(Float64, y)))

Base.cconvert normally just calls convert, 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 handles conversion to Ptr 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, a review of some relevant Julia type terminology:

Syntax / Keyword Example Description
mutable struct BitSet "Leaf Type" :: A group of related data that includes a type-tag, is managed by the Julia GC, and is defined by object-identity. The type parameters of a leaf type must be fully defined (no TypeVars are allowed) in order for the instance to be constructed.
abstract type Any, AbstractArray{T, N}, Complex{T} "Super Type" :: A super-type (not a leaf-type) that cannot be instantiated, but can be used to describe a group of types.
T{A} Vector{Int} "Type Parameter" :: A specialization of a type (typically used for dispatch or storage optimization).
"TypeVar" :: The T in the type parameter declaration is referred to as a TypeVar (short for type variable).
primitive type Int, Float64 "Primitive Type" :: A type with no fields, but a size. It is stored and defined by-value.
struct Pair{Int, Int} "Struct" :: A type with all fields defined to be constant. It is defined by-value, and may be stored with a type-tag.
ComplexF64 (isbits) "Is-Bits" :: A primitive type, or a struct type where all fields are other isbits types. It is defined by-value, and is stored without a type-tag.
struct ...; end nothing "Singleton" :: a Leaf Type or Struct with no fields.
(...) or tuple(...) (1, 2, 3) "Tuple" :: an immutable data-structure similar to an anonymous struct type, or a constant array. Represented as either an array or a struct.

[Bits Types](@id man-bits-types)

There are several special types to be aware of, as no other type can be defined to behave the same:

  • Float32

    Exactly corresponds to the float type in C (or REAL*4 in Fortran).

  • Float64

    Exactly corresponds to the double type in C (or REAL*8 in Fortran).

  • ComplexF32

    Exactly corresponds to the complex float type in C (or COMPLEX*8 in Fortran).

  • ComplexF64

    Exactly corresponds to the complex double type in C (or COMPLEX*16 in Fortran).

  • Signed

    Exactly corresponds to the signed type annotation in C (or any INTEGER type in Fortran). Any Julia type that is not a subtype of Signed is assumed to be unsigned.

  • Ref{T}

    Behaves like a Ptr{T} that can manage its memory via the Julia GC.

  • Array{T,N}

    When an array is passed to C as a Ptr{T} argument, it is not reinterpret-cast: Julia requires that the element type of the array matches T, and the address of the first element is passed.

    Therefore, if an Array contains data in the wrong format, it will have to be explicitly converted using a call such as trunc(Int32, a).

    To pass an array A as a pointer of a different type without converting the data beforehand (for example, to pass a Float64 array to a function that operates on uninterpreted bytes), you can declare the argument as Ptr{Cvoid}.

    If an array of eltype Ptr{T} is passed as a Ptr{Ptr{T}} argument, Base.cconvert will attempt to first make a null-terminated copy of the array with each element replaced by its Base.cconvert version. This allows, for example, passing an argv pointer array of type Vector{String} to an argument of type Ptr{Ptr{Cchar}}.

On all systems we currently support, basic C/C++ value types may be translated to Julia types as follows. Every C type also has a corresponding Julia type with the same name, prefixed by C. This can help for writing portable code (and remembering that an int in C is not the same as an Int in Julia).

System Independent:

C name Fortran name Standard Julia Alias Julia Base Type
unsigned char CHARACTER Cuchar UInt8
bool (only in C++) Cuchar UInt8
short INTEGER*2, LOGICAL*2 Cshort Int16
unsigned short   Cushort UInt16
int, BOOL (C, typical) INTEGER*4, LOGICAL*4 Cint Int32
unsigned int   Cuint UInt32
long long INTEGER*8, LOGICAL*8 Clonglong Int64
unsigned long long   Culonglong UInt64
intmax_t   Cintmax_t Int64
uintmax_t   Cuintmax_t UInt64
float REAL*4i Cfloat Float32
double REAL*8 Cdouble Float64
complex float COMPLEX*8 ComplexF32 Complex{Float32}
complex double COMPLEX*16 ComplexF64 Complex{Float64}
ptrdiff_t   Cptrdiff_t Int
ssize_t   Cssize_t Int
size_t   Csize_t UInt
void     Cvoid
void and [[noreturn]] or _Noreturn     Union{}
void*     Ptr{Cvoid}
T* (where T represents an appropriately defined type)     Ref{T}
char* (or char[], e.g. a string) CHARACTER*N   Cstring if NUL-terminated, or Ptr{UInt8} if not
char** (or *char[])     Ptr{Ptr{UInt8}}
jl_value_t* (any Julia Type)     Any
jl_value_t** (a reference to a Julia Type)     Ref{Any}
va_arg     Not supported
... (variadic function specification)     T... (where T is one of the above types, variadic functions of different argument types are not supported)

The Cstring type is essentially a synonym for Ptr{UInt8}, except the conversion to Cstring throws an error if the Julia string contains any embedded NUL characters (which would cause the string to be silently truncated if the C routine treats NUL as the terminator). If you are passing a char* to a C routine that does not assume NUL termination (e.g. because you pass an explicit string length), or if you know for certain that your Julia string does not contain NUL and want to skip the check, you can use Ptr{UInt8} as the argument type. Cstring can also be used as the ccall 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:

C name Standard Julia Alias Julia Base Type
char Cchar Int8 (x86, x86_64), UInt8 (powerpc, arm)
long Clong Int (UNIX), Int32 (Windows)
unsigned long Culong UInt (UNIX), UInt32 (Windows)
wchar_t Cwchar_t Int32 (UNIX), UInt16 (Windows)

!!! note When calling Fortran, all inputs must be passed by pointers to heap- or stack-allocated values, so all type correspondences above should contain an additional Ptr{..} or Ref{..} wrapper around their type specification.

!!! warning For string arguments (char*) the Julia type should be Cstring (if NUL- terminated data is expected) or either Ptr{Cchar} or Ptr{UInt8} otherwise (these two pointer types have the same effect), as described above, not String. Similarly, for array arguments (T[] or T*), the Julia type should again be Ptr{T}, not Vector{T}.

!!! warning Julia's Char type is 32 bits, which is not the same as the wide character type (wchar_t or wint_t) on all platforms.

!!! warning A return type of Union{} means the function will not return i.e. C++11 [[noreturn]] or C11 _Noreturn (e.g. jl_throw or longjmp). Do not use this for functions that return no value (void) but do return, use Cvoid instead.

!!! note For wchar_t* arguments, the Julia type should be Cwstring (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}} type within Julia. For example, C functions of the form:

```c
int main(int argc, char **argv);
```

can be called via the following Julia code:

```julia
argv = [ "a.out", "arg1", "arg2" ]
ccall(:main, Int32, (Int32, Ptr{Ptr{UInt8}}), length(argv), argv)
```

!!! note For Fortran functions taking variable length strings of type character(len=*) the string lengths are provided as hidden arguments. Type and position of these arguments in the list are compiler specific, where compiler vendors usually default to using Csize_t as type and append the hidden arguments at the end of the argument list. While this behaviour is fixed for some compilers (GNU), others optionally permit placing hidden arguments directly after the character argument (Intel,PGI). For example, Fortran subroutines of the form

```fortran
subroutine test(str1, str2)
character(len=*) :: str1,str2
```

can be called via the following Julia code, where the lengths are appended

```julia
str1 = "foo"
str2 = "bar"
ccall(:test, Void, (Ptr{UInt8}, Ptr{UInt8}, Csize_t, Csize_t),
                    str1, str2, sizeof(str1), sizeof(str2))
```

!!! warning Fortran compilers may also add other hidden arguments for pointers, assumed-shape (:) and assumed-size (*) arrays. Such behaviour can be avoided by using ISO_C_BINDING and including bind(c) in the definition of the subroutine, which is strongly recommended for interoperable code. In this case there will be no hidden arguments, at the cost of some language features (e.g. only character(len=1) will be permitted to pass strings).

!!! note A C function declared to return Cvoid will return the value nothing in Julia.

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 field layout.

When used recursively, isbits types are stored inline. 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 isbits struct 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.

You can get a near 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:

in C:

struct B {
    int A[3];
};
b_a_2 = B.A[2];

in 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)

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:

struct String {
    int strlen;
    char data[];
};

In Julia, we can access the parts independently to make a copy of that string:

str = from_c::Ptr{Cvoid}
len = unsafe_load(Ptr{Cint}(str))
unsafe_string(str + Core.sizeof(Cint), len)

Type Parameters

The type arguments to ccall and @cfunction are evaluated statically, when the method containing the usage is defined. They therefore must take the form of a literal tuple, not a variable, and cannot reference local variables.

This may sound like a strange restriction, but remember that since C is not a dynamic language like Julia, its functions can only accept argument types with a statically-known, fixed signature.

However, while the type layout must be known statically to compute the intended C ABI, the static parameters of the function are considered to be part of this static environment. The static parameters of the function may be used as type parameters in the call signature, as long as they don't affect the layout of the type. For example, f(x::T) where {T} = ccall(:valid, Ptr{T}, (Ptr{T},), x) is valid, since Ptr is always a word-size primitive type. But, g(x::T) where {T} = ccall(:notvalid, T, (T,), x) is not valid, since the type layout of T is not known statically.

SIMD Values

Note: This feature is currently implemented on 64-bit x86 and AArch64 platforms only.

If a C/C++ routine has an argument or return value that is a native SIMD type, the corresponding Julia type is a homogeneous tuple of VecElement that naturally maps to the SIMD type. Specifically:

  • The tuple must be the same size as the SIMD type. For example, a tuple representing an __m128 on x86 must have a size of 16 bytes.
  • The element type of the tuple must be an instance of VecElement{T} where T is a primitive type that is 1, 2, 4 or 8 bytes.

For instance, consider this C routine that uses AVX intrinsics:

#include <immintrin.h>

__m256 dist( __m256 a, __m256 b ) {
    return _mm256_sqrt_ps(_mm256_add_ps(_mm256_mul_ps(a, a),
                                        _mm256_mul_ps(b, b)));
}

The following Julia code calls dist using ccall:

const m256 = NTuple{8, VecElement{Float32}}

a = m256(ntuple(i -> VecElement(sin(Float32(i))), 8))
b = m256(ntuple(i -> VecElement(cos(Float32(i))), 8))

function call_dist(a::m256, b::m256)
    ccall((:dist, "libdist"), m256, (m256, m256), a, b)
end

println(call_dist(a,b))

The host machine must have the requisite SIMD registers. For example, the code above will not work on hosts without AVX support.

Memory Ownership

malloc/free

Memory allocation and deallocation of such objects must be handled by calls to the appropriate cleanup routines in the libraries being used, just like in any C program. Do not try to free an object received from a C library with Libc.free in Julia, as this may result 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.

When to use T, Ptr{T} and Ref{T}

In Julia code wrapping calls to external C routines, ordinary (non-pointer) data should be declared to be of type T inside the ccall, as they are passed by value. For C code accepting pointers, Ref{T} 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. In contrast, pointers returned by the C function called should be declared to be of output type Ptr{T}, 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.

In Julia code wrapping calls to external Fortran routines, all input arguments should be declared as of type Ref{T}, as Fortran passes all variables by pointers to memory locations. The return type should either be Cvoid for Fortran subroutines, or a T for Fortran functions returning the type T.

Mapping C Functions to Julia

ccall / @cfunction argument translation guide

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 or any of their typedef equivalents

    • T, where T is an equivalent Julia Bits Type (per the table above)
    • if T is an enum, the argument type should be equivalent to Cint or Cuint
    • argument value will be copied (passed by value)
  • struct T (including typedef to a struct)

    • T, where T is a Julia leaf type
    • argument value will be copied (passed by value)
  • void*

    • depends on how this parameter is used, first translate this to the intended pointer type, then determine the Julia equivalent using the remaining rules in this list
    • this argument may be declared as Ptr{Cvoid}, if it really is just an unknown pointer
  • jl_value_t*

    • Any
    • argument value must be a valid Julia object
  • jl_value_t**

    • Ref{Any}
    • argument value must be a valid Julia object (or C_NULL)
  • T*

    • Ref{T}, where T is the Julia type corresponding to T
    • argument value will be copied if it is an isbits type otherwise, the value must be a valid Julia object
  • T (*)(...) (e.g. a pointer to a function)

    • Ptr{Cvoid} (you may need to use @cfunction explicitly to create this pointer)
  • ... (e.g. a vararg)

    • T..., where T is the Julia type
    • currently unsupported by @cfunction
  • va_arg

    • not supported by ccall or @cfunction

ccall / @cfunction return type translation guide

For translating a C return type to Julia:

  • void

    • Cvoid (this will return the singleton instance nothing::Cvoid)
  • T, where T is one of the primitive types: char, int, long, short, float, double, complex, enum or any of their typedef equivalents

    • T, where T is an equivalent Julia Bits Type (per the table above)
    • if T is an enum, the argument type should be equivalent to Cint or Cuint
    • argument value will be copied (returned by-value)
  • struct T (including typedef to a struct)

    • T, where T is a Julia Leaf Type
    • argument value will be copied (returned by-value)
  • void*

    • depends on how this parameter is used, first translate this to the intended pointer type, then determine the Julia equivalent using the remaining rules in this list
    • this argument may be declared as Ptr{Cvoid}, if it really is just an unknown pointer
  • jl_value_t*

    • Any
    • argument value must be a valid Julia object
  • jl_value_t**

    • Ptr{Any} (Ref{Any} is invalid as a return type)
    • argument value must be a valid Julia object (or C_NULL)
  • T*

    • 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**)
      • C MUST NOT modify the memory returned via Ref{T} if T is an isbits type
    • If the memory is owned by C:

      • Ptr{T}, where T is the Julia type corresponding to T
  • T (*)(...) (e.g. a pointer to a function)

    • Ptr{Cvoid} (you may need to use @cfunction explicitly to create this pointer)

Passing Pointers for Modifying Inputs

Because C doesn't support multiple return values, often C functions will take pointers to data that the function will modify. To accomplish this within a ccall, you need to first encapsulate the value inside a Ref{T} of the appropriate type. When you pass this Ref object as an argument, Julia will automatically pass a C pointer to the encapsulated data:

width = Ref{Cint}(0)
range = Ref{Cfloat}(0)
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 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.

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

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

mutable struct 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), # name of C function and library
        Ptr{gsl_permutation},              # output type
        (Csize_t,),                        # tuple of input types
        n                                  # name of Julia variable to pass in
    )
    if output_ptr == C_NULL # Could not allocate memory
        throw(OutOfMemoryError())
    end
    return output_ptr
end

The GNU Scientific 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. (If the wrapper was calling a Fortran function instead, the corresponding function input signature should 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 implicitly calls Base.cconvert(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), # name of C function and library
        Cvoid,                             # output type
        (Ref{gsl_permutation},),          # tuple of input types
        p                                 # name of Julia variable to pass in
    )
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 and therefore can be used in the same (covariant) context of the input argument to a ccall. 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.

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 is also acceptable.

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 = Vector{Cdouble}(undef, nmax - nmin + 1)
    errorcode = ccall(
        (:gsl_sf_bessel_Jn_array, :libgsl), # name of C function and library
        Cint,                               # output type
        (Cint, Cint, Cdouble, Ref{Cdouble}),# tuple of input types
        nmin, nmax, x, result_array         # names of Julia variables to pass in
    )
    if errorcode != 0
        error("GSL error code $errorcode")
    end
    return result_array
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) 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 executes. If Ptr{Cdouble} were used instead, the ccall 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.

Garbage Collection Safety

When passing data to a ccall, it is best to avoid using the pointer function. Instead define a convert method and pass the variables directly to the ccall. ccall automatically arranges that all of its arguments will be preserved from garbage collection until the call returns. If a C API will store a reference to memory allocated by Julia, after the ccall returns, you must arrange that the object remains visible to the garbage collector. The suggested way to handle this is to make a global variable of type Array{Ref,1} to hold these values, until 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 and String 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 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 that b would be finalized after a. If proper finalization of a depends on b being valid, it must be handled in other ways.

Non-constant Function Specifications

A (name, library) function specification must be a constant expression. However, it is possible to use computed values as function names by staging through eval as follows:

@eval ccall(($(string("a", "b")), "lib"), ...

This expression constructs a name using string, then substitutes this name into a new ccall expression, which is then evaluated. Keep in mind that eval only operates at the top level, so within this expression local variables will not be available (unless their values are substituted with $). For this reason, eval is typically only used to form top-level definitions, for example when wrapping libraries that contain many similar functions. A similar example can be constructed for @cfunction.

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

The first argument to ccall can also be an expression evaluated at run time. In this case, the expression must evaluate to a Ptr, which will be used as the address of the native function to call. This behavior occurs when the first ccall argument contains references to non-constants, such as local variables, function arguments, or non-constant globals.

For example, you might look up the function via dlsym, then cache it in a shared reference for that session. For example:

macro dlsym(func, lib)
    z = Ref{Ptr{Cvoid}}(C_NULL)
    quote
        let zlocal = $z[]
            if zlocal == C_NULL
                zlocal = dlsym($(esc(lib))::Ptr{Cvoid}, $(esc(func)))::Ptr{Cvoid}
                $z[] = $zlocal
            end
            zlocal
        end
    end
end

mylibvar = Libdl.dlopen("mylib")
ccall(@dlsym("myfunc", mylibvar), Cvoid, ())

Closure cfunctions

The first argument to @cfunction can be marked with a $, in which case the return value will instead be a struct CFunction which closes over the argument. You must ensure that this return object is kept alive until all uses of it are done. The contents and code at the cfunction pointer will be erased via a finalizer when this reference is dropped and atexit. This is not usually needed, since this functionality is not present in C, but can be useful for dealing with ill-designed APIs which don't provide a separate closure environment parameter.

function qsort(a::Vector{T}, cmp) where T
    isbits(T) || throw(ArgumentError("this method can only qsort isbits arrays"))
    callback = @cfunction $cmp Cint (Ref{T}, Ref{T})
    # Here, `callback` isa Base.CFunction, which will be converted to Ptr{Cvoid}
    # (and protected against finalization) by the ccall
    ccall(:qsort, Cvoid, (Ptr{T}, Csize_t, Csize_t, Ptr{Cvoid}),
        a, length(a), Base.elsize(a), callback)
    # We could instead use:
    #    GC.@preserve callback begin
    #        use(Base.unsafe_convert(Ptr{Cvoid}, callback))
    #    end
    # if we needed to use it outside of a `ccall`
    return a
end

Closing a Library

It is sometimes useful to close (unload) a library so that it can be reloaded. For instance, when developing C code for use with Julia, one may need to compile, call the C code from Julia, then close the library, make an edit, recompile, and load in the new changes. One can either restart Julia or use the Libdl functions to manage the library explicitly, such as:

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.

Note that when using ccall with the tuple input (e.g., ccall((:my_fcn, "./my_lib.so"), ...)), the library is opened implicitly and it may not be explicitly closed.

Calling Convention

The second argument to ccall 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 gethostnameccall as above, but with the correct signature for Windows:

hn = Vector{UInt8}(undef, 256)
err = ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn))

For more information, please see the LLVM Language Reference.

There is one additional special calling convention [llvmcall](@ref Base.llvmcall), which allows inserting calls to LLVM intrinsics directly. This can be especially useful when targeting unusual platforms such as GPGPUs. For example, for CUDA, we need to be able to read the thread index:

ccall("llvm.nvvm.read.ptx.sreg.tid.x", llvmcall, Int32, ())

As with any ccall, it is essential to get the argument signature exactly correct. Also, note that there is no compatibility layer that ensures the intrinsic makes sense and works on the current target, unlike the equivalent Julia functions exposed by Core.Intrinsics.

Accessing Global Variables

Global variables exported by native libraries can be accessed by name using the cglobal function. The arguments to cglobal are a symbol specification identical to that used by ccall, and a type describing the value stored in the variable:

julia> cglobal((:errno, :libc), Int32)
Ptr{Int32} @0x00007f418d0816b8

The result is a pointer giving the address of the value. The value can be manipulated through this pointer using unsafe_load and unsafe_store!.

Accessing Data through a Pointer

The following methods are described as "unsafe" because a bad pointer or type declaration can cause Julia to terminate abruptly.

Given a Ptr{T}, the contents of type T can generally be copied from the referenced memory into a Julia object using unsafe_load(ptr, [index]). The index argument is optional (default is 1), and follows the Julia-convention of 1-based indexing. This function is intentionally similar to the behavior of getindex and setindex! (e.g. [] access syntax).

The return value will be a new object initialized to contain a copy of the contents of the referenced memory. The referenced memory can safely be freed or released.

If T is Any, then the memory is assumed to contain a reference to a Julia object (a jl_value_t*), the result will be a reference to this object, and the object will not be copied. You must be careful in this case to ensure that the object was always visible to the garbage collector (pointers do not count, but the new reference does) to ensure the memory is not prematurely freed. Note that if the object was not originally allocated by Julia, the new object will never be finalized by Julia's garbage collector. If the Ptr itself is actually a jl_value_t*, it can be converted back to a Julia object reference by unsafe_pointer_to_objref(ptr). (Julia values v can be converted to jl_value_t* pointers, as Ptr{Cvoid}, by calling pointer_from_objref(v).)

The reverse operation (writing data to a Ptr{T}), can be performed using unsafe_store!(ptr, value, [index]). 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) 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 all access is complete.

Arithmetic on the Ptr type in Julia (e.g. using +) does not behave the same as C's pointer arithmetic. Adding an integer to a Ptr in Julia always moves the pointer by some number of bytes, not elements. This way, the address values obtained from pointer arithmetic do not depend on the element types of pointers.

Thread-safety

Some C libraries execute their callbacks from a different thread, and since Julia isn't thread-safe you'll need to take some extra precautions. In particular, you'll need to set up a two-layered system: the C callback should only schedule (via Julia's event loop) the execution of your "real" callback. To do this, create an [AsyncCondition](@ref Base.AsyncCondition) object and wait on it:

cond = Base.AsyncCondition()
wait(cond)

The callback you pass to C should only execute a ccall to :uv_async_send, passing cond.handle as the argument, taking care to avoid any allocations or other interactions with the Julia runtime.

Note that events may be coalesced, so multiple calls to uv_async_send may result in a single wakeup notification to the condition.

More About Callbacks

For more details on how to pass callbacks to C libraries, see this blog post.

C++

For direct C++ interfacing, see the Cxx package. For tools to create C++ bindings, see the CxxWrap package.