CBinding.jl
Automatic C interfacing for Julia
Install / Use
/learn @analytech-solutions/CBinding.jlREADME
CBinding.jl
Use CBinding.jl to automatically create C library bindings with Julia at runtime!
Package supports these C features:
- [x] fully supports C's
struct,union, andenumtypes - [x] alignment strategies
- [x] bit fields
- [x] nested types
- [x] anonymous types
- [x] type qualifiers
- [x] variadic functions
- [x] unknown-length arrays
- [x] inline functions (experimental opt-in)
- [x] typed function pointers
- [x] function calling conventions
- [x] automatic callback function pointers
- [x] documentation generation
- [x] preprocessor macros (partially supported)
- [x] fully supports insane C (i.e.
extern struct { int i; } g[2], func();)
Read on to learn how to automatically create C library bindings, or learn how to use the generated bindings.
Create bindings with CBinding.jl
First, set up a compiler context to collect C expressions (at the module scope, or at the REPL).
julia> using CBinding
julia> c``
Notice that c`...` is a command macro (with the backticks) and is the means of specifying command line arguments to the Clang parser.
Each time such a command macro is used, a new compiler context is started for the module creating it.
A more real-life example might look like:
julia> libpath = find_libpath();
julia> c`-std=c99 -Wall -DGO_FAST=1 -Imylib/include -L$(libpath) -lmylib`
The compiler context also finds the paths of all specified libraries so it can use them in any bindings that are created.
Next the c"..." string macro can be used to input C code and automatically create the equivalent Julia types, global variable bindings, and function bindings.
It is often the case that the C code will span multiple lines, so the triple-quoted variant (c"""...""") is most effective for this usage.
julia> c"""
struct S;
struct T {
int i;
struct S *s;
struct T *t;
};
extern void func(struct S *s, struct T t);
""";
That's it... That's all that is needed to create a couple C types and a function binding in Julia, but actually, it gets even easier!
C API's usually come with header files, so let's just use those to create the Julia bindings and save some effort.
By default, bindings are generated from the code directly written in C string macros and header files explicitly included in them, but not headers included by those headers.
See the i string macro option to allow parsing certain implicitly included headers as well.
julia> c"""
#include <mylib/header.h>
""";
- [x] all C types are defined in Julia
- [x] C function and global variable bindings defined
- [x] the C API is documented and exported by the enclosing module
All done in just a few lines of code! Take a look at the complete example below or continue reading to learn about some more details.
Some gory details
The C expressions are parsed and immediately converted to Julia code.
In fact, the generated Julia code can be inspected using @macroexpand, like this:
julia> @macroexpand c"""
struct S;
struct T {
int i;
struct S *s;
struct T *t;
};
extern void func(struct S *s, struct T t);
"""
⋮
YIKES!
⋮
In order to support the fully automatic conversion and avoid name collisions, the names of C types or functions are mangled a bit to work in Julia.
Therefore everything generated by CBinding.jl can be accessed with the c"..." string macro (more about this below) to indicate that it lives in C-land.
As an example, the function func above is available in Julia as c"func".
It is possible to store the generated bindings to more user-friendly names (this can sometimes be automated, see the j option).
Placing each C declaration in its own macro helps when doing this manually, like:
julia> const S = c"""
struct S;
""";
julia> const T = c"""
struct T {
int i;
struct S *s;
struct T *t;
};
""";
julia> c"""
extern void func(struct S *s, struct T t);
"""j;
Constructs from the standard C library headers are currently not being emitted by CBinding.jl, but other packages may be developed to provide a unified source for them.
For now, dependencies on C library or other libraries should be placed before any C code blocks referencing them.
Most often it is only a few using and const statements.
A complete example
Finally, a set of examples can be found at https://github.com/analytech-solutions/ExamplesUsingCBinding.jl, but here is a generalized example of what a package using CBinding.jl might look like:
module LibFoo
module libfoo
import Foo_jll
using CBinding
# libfoo has libbar as a dep, and LibBar has bindings for it
using LibBar: libbar
# set up the parser
let
incdir = joinpath(Foo_jll.artifact_dir, "include")
libdir = dirname(Foo_jll.libfoo_path)
c`-std=c99 -fparse-all-comments -I$(incdir) -L$(libdir) -lfoo`
end
# libfoo refers to some std C sized types (eventually made available with something like `using C99`)
const c"int32_t" = Int32
const c"int64_t" = Int64
const c"uint32_t" = UInt32
const c"uint64_t" = UInt64
# generate bindings for libfoo
c"""
#include <libfoo/header-1.h>
#include <libfoo/header-2.h>
"""
# any other bindings not in headers
c"""
struct FooStruct {
struct BarStruct bs;
};
extern struct FooStruct *foo_like_its_the_80s(int i);
"""
end
# high-level Julian interface to libfoo
using CBinding
using .libfoo
function foo(i)
ptr = c"foo_like_its_the_80s"(Cint(i-1))
try
return JulianFoo(ptr[])
finally
Libc.free(ptr)
end
end
end
Options for c"..."
The string macro has some options to handle more complex use cases. Occasionally it is necessary to include or define C code that is just a dependency and should not be exported or perhaps excluded from the generated bindings altogether. These kinds of situations can be handled with combinations of the following string macro suffixes.
d- defer conversion of the C code block; successive blocks marked withdwill keep deferring until a block without it (its options will be used for processing the deferred blocks)f- don't create bindings forexternfunctionsi- also parse implicitly included headers that are related (in the same directory or subdirectories) to explicitly included headersj- provide additional bindings using Julian names (name collisions likely)J- provide additional bindings using Julian names with annotated user-defined types (usingstruct_,union_, orenum_prefixes)m- skip conversion of C macrosn- show warnings for macros or inline functions that are skipped (and other conversion issues)p- mark the C code as "private" content that will not be exportedq- quietly parse the block of C code, suppressing any compiler/linker messagesr- the C code is only a reference to something in C-land and bindings are not to be generateds- skip processing of this block of C codet- skip conversion of C typesu- leave this block of C code undocumentedv- don't create bindings forexternvariablesw- create bindings for inline functions by using wrapper libraries (somewhat experimental)
julia> c"""
#include <stdio.h> // provides FILE type, but skip emitting bindings for this block
"""s;
julia> c"""
struct File { // do not include this type in module exports, and suppress compiler messages
FILE *f;
};
"""pq;
Using CBinding.jl-generated bindings
The c"..." string macro can be used to refer to any of the types, global variables, or functions generated by CBinding.jl.
When simply referencing the C content, setting up a compiler context (i.e. using c`...`) is not necessary.
The c"..." string macro can take on two meanings depending on the content placed in it.
So to guarantee it is interpreted as a reference to something in C, rather than a block of C code to create bindings with, include an r in the string macro options.
julia> module MyLib # generally some C bindings are defined elsewhere
using CBinding
c`-std=c99 -Wall -Imy/include`
c"""
struct S;
struct T {
int i;
struct S *s;
struct T *t;
};
extern void func(struct S *s, struct T t);
"""
end
julia> using CBinding, .MyLib
julia> c"struct T" <: Cstruct
true
julia> c"struct T"r <: Cstruct # use 'r' option to guarantee it is treated as a reference
true
julia> t = c"struct T"(i = 123);
julia> t.i
123
The user-defined types (enum, struct, and union) are referenced just as they are in C (e.g. c"enum E", c"struct S", and c"union U").
All other types, pointers, arrays, global variables, enumeration constants, functions, etc. are also referenced just as they are in C.
Here is a quick reference for C string macro usage:
c"int"- theCinttypec"int[2]"- a length-2 static array ofCint'sc"int[2][4]"- a length-2 static array of length-4 static arrays ofCint'sc"int *"- pointer to aCintc"int **"- pointer to a pointer to aCintc"int const **"- pointer to a pointer to a read-onlyCintc"enum MyUnion"- a user-defined Cenumtype- `c"un
Related Skills
node-connect
343.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
90.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
