This is a repository for demonstration purpose of how to wrap a C++ library and use it in Julia. We created C API interface for a library called simpson and wrapped it into the Simpson.jl
package we have here, while artifacts needed for interfacing between C++ and Julia code are stored under simpson_jll.jl package.
Simpson.jl
├── example
├── LICENSE
├── Manifest.toml
├── Project.toml
├── README.md
├── res
│ ├── Manifest.toml
│ ├── Project.toml
│ ├── prologue.jl # commands to be added in resulting api.jl
│ ├── wrap.jl # Clang.jl generates src/api.jl from it
│ └── wrap.toml # specification for autogeneration
├── src
│ ├── api.jl # low-level Julia API
│ └── Simpson.jl # high-level Julia API
└── test
├── assets
├── runtests.jl # includes sub-tests
└── test_integration1D.jl # an unit test
A example unit test is provided, make sure you are in the project environment when accessing interactive shell using julia --project
. Then execute test within the package environment and run test
$ julia --project
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.9.1 (2023-06-07)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> ]
(Simpson) pkg>
(Simpson) pkg> test
The following output is expected
Testing Running tests...
Test Summary: | Pass Total Time
Reference test: simpson integration rule 1D | 1 1 0.0s
- C++ library which contains a C API
- Artifacts of the C++ library stored in a JLL package
- Autogeneration of a low-level Julia API
api.jl
containingccall
to C++ functions using artifacts- Implementation of a high-level Julia API
The source C++ library needs to have a C API implemented for exporting C++ functions. Besides of this, it needs no special modifications.
-
simpson/include/c_api.h
-
simpson/src/c_api.cpp
Besides of these, no other modifications need to be done.
For dynamic linker of Julia to be able to use C++ code we have written, we need to compile our C++ library into a .so
file as a shared library. Additionally, we also need the C API header c_api.h
to tell the linker necessary information such as which symbols it should look up and at which line of the compiled binary file the function to be executed resides.
In Julia community, these artifacts are stored under individual JLL
package. Under the JuliaBinaryWrappers organization, you could find a lot of existing packages with source code in other languages being hosted there.
In order to create such JLL
packages, you firstly need to use BinaryBuilder.jl to create a build_tarballs.jl
file that contains build instructions for your specific library. One can refer to the BinaryBuilder tutorial for details. Then we can now use the created script to automate the process for building artifacts targeting different platforms.
There are mainly two ways to deploy your project.
- Submitting the build script to Yggdrasil and host the built artifacts under JuliaBinaryWrappers organization.
- Publish your build script locally under your github account without going through Ygdrasil.
In general, projects submitted to Yggdrasil here should be relevant to many users. So in our case we would choose the latter option and deploy the JLL package simpson_jll.jl locally.
3. Autogeneration using Clang.jl
Next, we need to use Clang.jl
for generating low-level API that contains ccall
to C-exported functions.
We firstly add the needed JLL package to our dependency using the package mode of the interactive shell. If the targeting JLL package has been submitted to Yggdrasil and is registered, ] add package_jll.jl
would do the job. Otherwise you need to point to the exact github repository it resides with commands like ] add github.com/youwuyou/simpson_jll.jl
.
The artifacts will be fetched and we can find them using the artifact_dir
variable.
julia> using simpson_jll
julia> simpson_jll.artifact_dir
"/path/to/.julia/artifacts/89430912394f19fb4ec96a345d5a6ef346eae22"
Now make sure you can find header files under artifact_dir
and then you can create your first Julia wrappers for C libraries from a collection of header files by following this Clang.jl tutorial! In Simpson.jl, we put relevant scripts into Clang.jl/res
folder and the autogenerated low-level API that contains Julia wrappers is contained in src/api.jl
that gets generated.
Congratulations! Now you can start with creating high-level wrapper functions by using low-level wrapper functions within src/api.jl
script, which we do Within src/Simpson.jl
.