95 lines
29 KiB
HTML
95 lines
29 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Embedding Julia · The Julia Language</title><script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||
|
||
ga('create', 'UA-28835595-6', 'auto');
|
||
ga('send', 'pageview');
|
||
</script><link href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.2.0/normalize.min.css" rel="stylesheet" type="text/css"/><link href="https://fonts.googleapis.com/css?family=Lato|Roboto+Mono" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css"/><script>documenterBaseURL=".."</script><script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.min.js" data-main="../assets/documenter.js"></script><script src="../siteinfo.js"></script><script src="../../versions.js"></script><link href="../assets/highlightjs/default.css" rel="stylesheet" type="text/css"/><link href="../assets/documenter.css" rel="stylesheet" type="text/css"/></head><body><nav class="toc"><a href="../index.html"><img class="logo" src="../assets/logo.png" alt="The Julia Language logo"/></a><h1>The Julia Language</h1><select id="version-selector" onChange="window.location.href=this.value" style="visibility: hidden"></select><form class="search" action="../search.html"><input id="search-query" name="q" type="text" placeholder="Search docs"/></form><ul><li><a class="toctext" href="../index.html">Home</a></li><li><span class="toctext">Manual</span><ul><li><a class="toctext" href="introduction.html">Introduction</a></li><li><a class="toctext" href="getting-started.html">Getting Started</a></li><li><a class="toctext" href="variables.html">Variables</a></li><li><a class="toctext" href="integers-and-floating-point-numbers.html">Integers and Floating-Point Numbers</a></li><li><a class="toctext" href="mathematical-operations.html">Mathematical Operations and Elementary Functions</a></li><li><a class="toctext" href="complex-and-rational-numbers.html">Complex and Rational Numbers</a></li><li><a class="toctext" href="strings.html">Strings</a></li><li><a class="toctext" href="functions.html">Functions</a></li><li><a class="toctext" href="control-flow.html">Control Flow</a></li><li><a class="toctext" href="variables-and-scoping.html">Scope of Variables</a></li><li><a class="toctext" href="types.html">Types</a></li><li><a class="toctext" href="methods.html">Methods</a></li><li><a class="toctext" href="constructors.html">Constructors</a></li><li><a class="toctext" href="conversion-and-promotion.html">Conversion and Promotion</a></li><li><a class="toctext" href="interfaces.html">Interfaces</a></li><li><a class="toctext" href="modules.html">Modules</a></li><li><a class="toctext" href="documentation.html">Documentation</a></li><li><a class="toctext" href="metaprogramming.html">Metaprogramming</a></li><li><a class="toctext" href="arrays.html">Multi-dimensional Arrays</a></li><li><a class="toctext" href="linear-algebra.html">Linear algebra</a></li><li><a class="toctext" href="networking-and-streams.html">Networking and Streams</a></li><li><a class="toctext" href="parallel-computing.html">Parallel Computing</a></li><li><a class="toctext" href="dates.html">Date and DateTime</a></li><li><a class="toctext" href="interacting-with-julia.html">Interacting With Julia</a></li><li><a class="toctext" href="running-external-programs.html">Running External Programs</a></li><li><a class="toctext" href="calling-c-and-fortran-code.html">Calling C and Fortran Code</a></li><li><a class="toctext" href="handling-operating-system-variation.html">Handling Operating System Variation</a></li><li><a class="toctext" href="environment-variables.html">Environment Variables</a></li><li class="current"><a class="toctext" href="embedding.html">Embedding Julia</a><ul class="internal"><li><a class="toctext" href="#High-Level-Embedding-1">High-Level Embedding</a></li><li><a class="toctext" href="#Converting-Types-1">Converting Types</a></li><li><a class="toctext" href="#Calling-Julia-Functions-1">Calling Julia Functions</a></li><li><a class="toctext" href="#Memory-Management-1">Memory Management</a></li><li><a class="toctext" href="#Working-with-Arrays-1">Working with Arrays</a></li><li><a class="toctext" href="#Exceptions-1">Exceptions</a></li></ul></li><li><a class="toctext" href="packages.html">Packages</a></li><li><a class="toctext" href="profile.html">Profiling</a></li><li><a class="toctext" href="stacktraces.html">Stack Traces</a></li><li><a class="toctext" href="performance-tips.html">Performance Tips</a></li><li><a class="toctext" href="workflow-tips.html">Workflow Tips</a></li><li><a class="toctext" href="style-guide.html">Style Guide</a></li><li><a class="toctext" href="faq.html">Frequently Asked Questions</a></li><li><a class="toctext" href="noteworthy-differences.html">Noteworthy Differences from other Languages</a></li><li><a class="toctext" href="unicode-input.html">Unicode Input</a></li></ul></li><li><span class="toctext">Standard Library</span><ul><li><a class="toctext" href="../stdlib/base.html">Essentials</a></li><li><a class="toctext" href="../stdlib/collections.html">Collections and Data Structures</a></li><li><a class="toctext" href="../stdlib/math.html">Mathematics</a></li><li><a class="toctext" href="../stdlib/numbers.html">Numbers</a></li><li><a class="toctext" href="../stdlib/strings.html">Strings</a></li><li><a class="toctext" href="../stdlib/arrays.html">Arrays</a></li><li><a class="toctext" href="../stdlib/parallel.html">Tasks and Parallel Computing</a></li><li><a class="toctext" href="../stdlib/linalg.html">Linear Algebra</a></li><li><a class="toctext" href="../stdlib/constants.html">Constants</a></li><li><a class="toctext" href="../stdlib/file.html">Filesystem</a></li><li><a class="toctext" href="../stdlib/io-network.html">I/O and Network</a></li><li><a class="toctext" href="../stdlib/punctuation.html">Punctuation</a></li><li><a class="toctext" href="../stdlib/sort.html">Sorting and Related Functions</a></li><li><a class="toctext" href="../stdlib/pkg.html">Package Manager Functions</a></li><li><a class="toctext" href="../stdlib/dates.html">Dates and Time</a></li><li><a class="toctext" href="../stdlib/iterators.html">Iteration utilities</a></li><li><a class="toctext" href="../stdlib/test.html">Unit Testing</a></li><li><a class="toctext" href="../stdlib/c.html">C Interface</a></li><li><a class="toctext" href="../stdlib/libc.html">C Standard Library</a></li><li><a class="toctext" href="../stdlib/libdl.html">Dynamic Linker</a></li><li><a class="toctext" href="../stdlib/profile.html">Profiling</a></li><li><a class="toctext" href="../stdlib/stacktraces.html">StackTraces</a></li><li><a class="toctext" href="../stdlib/simd-types.html">SIMD Support</a></li></ul></li><li><span class="toctext">Developer Documentation</span><ul><li><a class="toctext" href="../devdocs/reflection.html">Reflection and introspection</a></li><li><span class="toctext">Documentation of Julia's Internals</span><ul><li><a class="toctext" href="../devdocs/init.html">Initialization of the Julia runtime</a></li><li><a class="toctext" href="../devdocs/ast.html">Julia ASTs</a></li><li><a class="toctext" href="../devdocs/types.html">More about types</a></li><li><a class="toctext" href="../devdocs/object.html">Memory layout of Julia Objects</a></li><li><a class="toctext" href="../devdocs/eval.html">Eval of Julia code</a></li><li><a class="toctext" href="../devdocs/callconv.html">Calling Conventions</a></li><li><a class="toctext" href="../devdocs/compiler.html">High-level Overview of the Native-Code Generation Process</a></li><li><a class="toctext" href="../devdocs/functions.html">Julia Functions</a></li><li><a class="toctext" href="../devdocs/cartesian.html">Base.Cartesian</a></li><li><a class="toctext" href="../devdocs/meta.html">Talking to the compiler (the <code>:meta</code> mechanism)</a></li><li><a class="toctext" href="../devdocs/subarrays.html">SubArrays</a></li><li><a class="toctext" href="../devdocs/sysimg.html">System Image Building</a></li><li><a class="toctext" href="../devdocs/llvm.html">Working with LLVM</a></li><li><a class="toctext" href="../devdocs/stdio.html">printf() and stdio in the Julia runtime</a></li><li><a class="toctext" href="../devdocs/boundscheck.html">Bounds checking</a></li><li><a class="toctext" href="../devdocs/locks.html">Proper maintenance and care of multi-threading locks</a></li><li><a class="toctext" href="../devdocs/offset-arrays.html">Arrays with custom indices</a></li><li><a class="toctext" href="../devdocs/libgit2.html">Base.LibGit2</a></li><li><a class="toctext" href="../devdocs/require.html">Module loading</a></li></ul></li><li><span class="toctext">Developing/debugging Julia's C code</span><ul><li><a class="toctext" href="../devdocs/backtraces.html">Reporting and analyzing crashes (segfaults)</a></li><li><a class="toctext" href="../devdocs/debuggingtips.html">gdb debugging tips</a></li><li><a class="toctext" href="../devdocs/valgrind.html">Using Valgrind with Julia</a></li><li><a class="toctext" href="../devdocs/sanitizers.html">Sanitizer support</a></li></ul></li></ul></li></ul></nav><article id="docs"><header><nav><ul><li>Manual</li><li><a href="embedding.html">Embedding Julia</a></li></ul><a class="edit-page" href="https://github.com/JuliaLang/julia/tree/d386e40c17d43b79fc89d3e579fc04547241787c/doc/src/manual/embedding.md"><span class="fa"></span> Edit on GitHub</a></nav><hr/><div id="topbar"><span>Embedding Julia</span><a class="fa fa-bars" href="#"></a></div></header><h1><a class="nav-anchor" id="Embedding-Julia-1" href="#Embedding-Julia-1">Embedding Julia</a></h1><p>As we have seen in <a href="calling-c-and-fortran-code.html#Calling-C-and-Fortran-Code-1">Calling C and Fortran Code</a>, Julia has a simple and efficient way to call functions written in C. But there are situations where the opposite is needed: calling Julia function from C code. This can be used to integrate Julia code into a larger C/C++ project, without the need to rewrite everything in C/C++. Julia has a C API to make this possible. As almost all programming languages have some way to call C functions, the Julia C API can also be used to build further language bridges (e.g. calling Julia from Python or C#).</p><h2><a class="nav-anchor" id="High-Level-Embedding-1" href="#High-Level-Embedding-1">High-Level Embedding</a></h2><p>We start with a simple C program that initializes Julia and calls some Julia code:</p><pre><code class="language-c">#include <julia.h>
|
||
|
||
int main(int argc, char *argv[])
|
||
{
|
||
/* required: setup the Julia context */
|
||
jl_init();
|
||
|
||
/* run Julia commands */
|
||
jl_eval_string("print(sqrt(2.0))");
|
||
|
||
/* strongly recommended: notify Julia that the
|
||
program is about to terminate. this allows
|
||
Julia time to cleanup pending write requests
|
||
and run all finalizers
|
||
*/
|
||
jl_atexit_hook(0);
|
||
return 0;
|
||
}</code></pre><p>In order to build this program you have to put the path to the Julia header into the include path and link against <code>libjulia</code>. For instance, when Julia is installed to <code>$JULIA_DIR</code>, one can compile the above test program <code>test.c</code> with <code>gcc</code> using:</p><pre><code class="language-none">gcc -o test -fPIC -I$JULIA_DIR/include/julia -L$JULIA_DIR/lib test.c -ljulia $JULIA_DIR/lib/julia/libstdc++.so.6</code></pre><p>Then if the environment variable <code>JULIA_HOME</code> is set to <code>$JULIA_DIR/bin</code>, the output <code>test</code> program can be executed.</p><p>Alternatively, look at the <code>embedding.c</code> program in the Julia source tree in the <code>examples/</code> folder. The file <code>ui/repl.c</code> program is another simple example of how to set <code>jl_options</code> options while linking against <code>libjulia</code>.</p><p>The first thing that has to be done before calling any other Julia C function is to initialize Julia. This is done by calling <code>jl_init</code>, which tries to automatically determine Julia's install location. If you need to specify a custom location, or specify which system image to load, use <code>jl_init_with_image</code> instead.</p><p>The second statement in the test program evaluates a Julia statement using a call to <code>jl_eval_string</code>.</p><p>Before the program terminates, it is strongly recommended to call <code>jl_atexit_hook</code>. The above example program calls this before returning from <code>main</code>.</p><div class="admonition note"><div class="admonition-title">Note</div><div class="admonition-text"><p>Currently, dynamically linking with the <code>libjulia</code> shared library requires passing the <code>RTLD_GLOBAL</code> option. In Python, this looks like:</p><pre><code class="language-none">>>> julia=CDLL('./libjulia.dylib',RTLD_GLOBAL)
|
||
>>> julia.jl_init.argtypes = []
|
||
>>> julia.jl_init()
|
||
250593296</code></pre></div></div><div class="admonition note"><div class="admonition-title">Note</div><div class="admonition-text"><p>If the julia program needs to access symbols from the main executable, it may be necessary to add <code>-Wl,--export-dynamic</code> linker flag at compile time on Linux in addition to the ones generated by <code>julia-config.jl</code> described below. This is not necessary when compiling a shared library.</p></div></div><h3><a class="nav-anchor" id="Using-julia-config-to-automatically-determine-build-parameters-1" href="#Using-julia-config-to-automatically-determine-build-parameters-1">Using julia-config to automatically determine build parameters</a></h3><p>The script <code>julia-config.jl</code> was created to aid in determining what build parameters are required by a program that uses embedded Julia. This script uses the build parameters and system configuration of the particular Julia distribution it is invoked by to export the necessary compiler flags for an embedding program to interact with that distribution. This script is located in the Julia shared data directory.</p><h4><a class="nav-anchor" id="Example-1" href="#Example-1">Example</a></h4><pre><code class="language-c">#include <julia.h>
|
||
|
||
int main(int argc, char *argv[])
|
||
{
|
||
jl_init();
|
||
(void)jl_eval_string("println(sqrt(2.0))");
|
||
jl_atexit_hook(0);
|
||
return 0;
|
||
}</code></pre><h4><a class="nav-anchor" id="On-the-command-line-1" href="#On-the-command-line-1">On the command line</a></h4><p>A simple use of this script is from the command line. Assuming that <code>julia-config.jl</code> is located in <code>/usr/local/julia/share/julia</code>, it can be invoked on the command line directly and takes any combination of 3 flags:</p><pre><code class="language-none">/usr/local/julia/share/julia/julia-config.jl
|
||
Usage: julia-config [--cflags|--ldflags|--ldlibs]</code></pre><p>If the above example source is saved in the file <code>embed_example.c</code>, then the following command will compile it into a running program on Linux and Windows (MSYS2 environment), or if on OS/X, then substitute <code>clang</code> for <code>gcc</code>.:</p><pre><code class="language-none">/usr/local/julia/share/julia/julia-config.jl --cflags --ldflags --ldlibs | xargs gcc embed_example.c</code></pre><h4><a class="nav-anchor" id="Use-in-Makefiles-1" href="#Use-in-Makefiles-1">Use in Makefiles</a></h4><p>But in general, embedding projects will be more complicated than the above, and so the following allows general makefile support as well – assuming GNU make because of the use of the <strong>shell</strong> macro expansions. Additionally, though many times <code>julia-config.jl</code> may be found in the directory <code>/usr/local</code>, this is not necessarily the case, but Julia can be used to locate <code>julia-config.jl</code> too, and the makefile can be used to take advantage of that. The above example is extended to use a Makefile:</p><pre><code class="language-none">JL_SHARE = $(shell julia -e 'print(joinpath(JULIA_HOME,Base.DATAROOTDIR,"julia"))')
|
||
CFLAGS += $(shell $(JL_SHARE)/julia-config.jl --cflags)
|
||
CXXFLAGS += $(shell $(JL_SHARE)/julia-config.jl --cflags)
|
||
LDFLAGS += $(shell $(JL_SHARE)/julia-config.jl --ldflags)
|
||
LDLIBS += $(shell $(JL_SHARE)/julia-config.jl --ldlibs)
|
||
|
||
all: embed_example</code></pre><p>Now the build command is simply <code>make</code>.</p><h2><a class="nav-anchor" id="Converting-Types-1" href="#Converting-Types-1">Converting Types</a></h2><p>Real applications will not just need to execute expressions, but also return their values to the host program. <code>jl_eval_string</code> returns a <code>jl_value_t*</code>, which is a pointer to a heap-allocated Julia object. Storing simple data types like <a href="../stdlib/numbers.html#Core.Float64"><code>Float64</code></a> in this way is called <code>boxing</code>, and extracting the stored primitive data is called <code>unboxing</code>. Our improved sample program that calculates the square root of 2 in Julia and reads back the result in C looks as follows:</p><pre><code class="language-c">jl_value_t *ret = jl_eval_string("sqrt(2.0)");
|
||
|
||
if (jl_typeis(ret, jl_float64_type)) {
|
||
double ret_unboxed = jl_unbox_float64(ret);
|
||
printf("sqrt(2.0) in C: %e \n", ret_unboxed);
|
||
}
|
||
else {
|
||
printf("ERROR: unexpected return type from sqrt(::Float64)\n");
|
||
}</code></pre><p>In order to check whether <code>ret</code> is of a specific Julia type, we can use the <code>jl_isa</code>, <code>jl_typeis</code>, or <code>jl_is_...</code> functions. By typing <code>typeof(sqrt(2.0))</code> into the Julia shell we can see that the return type is <a href="../stdlib/numbers.html#Core.Float64"><code>Float64</code></a> (<code>double</code> in C). To convert the boxed Julia value into a C double the <code>jl_unbox_float64</code> function is used in the above code snippet.</p><p>Corresponding <code>jl_box_...</code> functions are used to convert the other way:</p><pre><code class="language-c">jl_value_t *a = jl_box_float64(3.0);
|
||
jl_value_t *b = jl_box_float32(3.0f);
|
||
jl_value_t *c = jl_box_int32(3);</code></pre><p>As we will see next, boxing is required to call Julia functions with specific arguments.</p><h2><a class="nav-anchor" id="Calling-Julia-Functions-1" href="#Calling-Julia-Functions-1">Calling Julia Functions</a></h2><p>While <code>jl_eval_string</code> allows C to obtain the result of a Julia expression, it does not allow passing arguments computed in C to Julia. For this you will need to invoke Julia functions directly, using <code>jl_call</code>:</p><pre><code class="language-c">jl_function_t *func = jl_get_function(jl_base_module, "sqrt");
|
||
jl_value_t *argument = jl_box_float64(2.0);
|
||
jl_value_t *ret = jl_call1(func, argument);</code></pre><p>In the first step, a handle to the Julia function <code>sqrt</code> is retrieved by calling <code>jl_get_function</code>. The first argument passed to <code>jl_get_function</code> is a pointer to the <code>Base</code> module in which <code>sqrt</code> is defined. Then, the double value is boxed using <code>jl_box_float64</code>. Finally, in the last step, the function is called using <code>jl_call1</code>. <code>jl_call0</code>, <code>jl_call2</code>, and <code>jl_call3</code> functions also exist, to conveniently handle different numbers of arguments. To pass more arguments, use <code>jl_call</code>:</p><pre><code class="language-none">jl_value_t *jl_call(jl_function_t *f, jl_value_t **args, int32_t nargs)</code></pre><p>Its second argument <code>args</code> is an array of <code>jl_value_t*</code> arguments and <code>nargs</code> is the number of arguments.</p><h2><a class="nav-anchor" id="Memory-Management-1" href="#Memory-Management-1">Memory Management</a></h2><p>As we have seen, Julia objects are represented in C as pointers. This raises the question of who is responsible for freeing these objects.</p><p>Typically, Julia objects are freed by a garbage collector (GC), but the GC does not automatically know that we are holding a reference to a Julia value from C. This means the GC can free objects out from under you, rendering pointers invalid.</p><p>The GC can only run when Julia objects are allocated. Calls like <code>jl_box_float64</code> perform allocation, and allocation might also happen at any point in running Julia code. However, it is generally safe to use pointers in between <code>jl_...</code> calls. But in order to make sure that values can survive <code>jl_...</code> calls, we have to tell Julia that we hold a reference to a Julia value. This can be done using the <code>JL_GC_PUSH</code> macros:</p><pre><code class="language-c">jl_value_t *ret = jl_eval_string("sqrt(2.0)");
|
||
JL_GC_PUSH1(&ret);
|
||
// Do something with ret
|
||
JL_GC_POP();</code></pre><p>The <code>JL_GC_POP</code> call releases the references established by the previous <code>JL_GC_PUSH</code>. Note that <code>JL_GC_PUSH</code> is working on the stack, so it must be exactly paired with a <code>JL_GC_POP</code> before the stack frame is destroyed.</p><p>Several Julia values can be pushed at once using the <code>JL_GC_PUSH2</code> , <code>JL_GC_PUSH3</code> , and <code>JL_GC_PUSH4</code> macros. To push an array of Julia values one can use the <code>JL_GC_PUSHARGS</code> macro, which can be used as follows:</p><pre><code class="language-c">jl_value_t **args;
|
||
JL_GC_PUSHARGS(args, 2); // args can now hold 2 `jl_value_t*` objects
|
||
args[0] = some_value;
|
||
args[1] = some_other_value;
|
||
// Do something with args (e.g. call jl_... functions)
|
||
JL_GC_POP();</code></pre><p>The garbage collector also operates under the assumption that it is aware of every old-generation object pointing to a young-generation one. Any time a pointer is updated breaking that assumption, it must be signaled to the collector with the <code>jl_gc_wb</code> (write barrier) function like so:</p><pre><code class="language-c">jl_value_t *parent = some_old_value, *child = some_young_value;
|
||
((some_specific_type*)parent)->field = child;
|
||
jl_gc_wb(parent, child);</code></pre><p>It is in general impossible to predict which values will be old at runtime, so the write barrier must be inserted after all explicit stores. One notable exception is if the <code>parent</code> object was just allocated and garbage collection was not run since then. Remember that most <code>jl_...</code> functions can sometimes invoke garbage collection.</p><p>The write barrier is also necessary for arrays of pointers when updating their data directly. For example:</p><pre><code class="language-c">jl_array_t *some_array = ...; // e.g. a Vector{Any}
|
||
void **data = (void**)jl_array_data(some_array);
|
||
jl_value_t *some_value = ...;
|
||
data[0] = some_value;
|
||
jl_gc_wb(some_array, some_value);</code></pre><h3><a class="nav-anchor" id="Manipulating-the-Garbage-Collector-1" href="#Manipulating-the-Garbage-Collector-1">Manipulating the Garbage Collector</a></h3><p>There are some functions to control the GC. In normal use cases, these should not be necessary.</p><table><tr><th>Function</th><th>Description</th></tr><tr><td><code>jl_gc_collect()</code></td><td>Force a GC run</td></tr><tr><td><code>jl_gc_enable(0)</code></td><td>Disable the GC, return previous state as int</td></tr><tr><td><code>jl_gc_enable(1)</code></td><td>Enable the GC, return previous state as int</td></tr><tr><td><code>jl_gc_is_enabled()</code></td><td>Return current state as int</td></tr></table><h2><a class="nav-anchor" id="Working-with-Arrays-1" href="#Working-with-Arrays-1">Working with Arrays</a></h2><p>Julia and C can share array data without copying. The next example will show how this works.</p><p>Julia arrays are represented in C by the datatype <code>jl_array_t*</code>. Basically, <code>jl_array_t</code> is a struct that contains:</p><ul><li><p>Information about the datatype</p></li><li><p>A pointer to the data block</p></li><li><p>Information about the sizes of the array</p></li></ul><p>To keep things simple, we start with a 1D array. Creating an array containing Float64 elements of length 10 is done by:</p><pre><code class="language-c">jl_value_t* array_type = jl_apply_array_type(jl_float64_type, 1);
|
||
jl_array_t* x = jl_alloc_array_1d(array_type, 10);</code></pre><p>Alternatively, if you have already allocated the array you can generate a thin wrapper around its data:</p><pre><code class="language-c">double *existingArray = (double*)malloc(sizeof(double)*10);
|
||
jl_array_t *x = jl_ptr_to_array_1d(array_type, existingArray, 10, 0);</code></pre><p>The last argument is a boolean indicating whether Julia should take ownership of the data. If this argument is non-zero, the GC will call <code>free</code> on the data pointer when the array is no longer referenced.</p><p>In order to access the data of x, we can use <code>jl_array_data</code>:</p><pre><code class="language-c">double *xData = (double*)jl_array_data(x);</code></pre><p>Now we can fill the array:</p><pre><code class="language-c">for(size_t i=0; i<jl_array_len(x); i++)
|
||
xData[i] = i;</code></pre><p>Now let us call a Julia function that performs an in-place operation on <code>x</code>:</p><pre><code class="language-c">jl_function_t *func = jl_get_function(jl_base_module, "reverse!");
|
||
jl_call1(func, (jl_value_t*)x);</code></pre><p>By printing the array, one can verify that the elements of <code>x</code> are now reversed.</p><h3><a class="nav-anchor" id="Accessing-Returned-Arrays-1" href="#Accessing-Returned-Arrays-1">Accessing Returned Arrays</a></h3><p>If a Julia function returns an array, the return value of <code>jl_eval_string</code> and <code>jl_call</code> can be cast to a <code>jl_array_t*</code>:</p><pre><code class="language-c">jl_function_t *func = jl_get_function(jl_base_module, "reverse");
|
||
jl_array_t *y = (jl_array_t*)jl_call1(func, (jl_value_t*)x);</code></pre><p>Now the content of <code>y</code> can be accessed as before using <code>jl_array_data</code>. As always, be sure to keep a reference to the array while it is in use.</p><h3><a class="nav-anchor" id="Multidimensional-Arrays-1" href="#Multidimensional-Arrays-1">Multidimensional Arrays</a></h3><p>Julia's multidimensional arrays are stored in memory in column-major order. Here is some code that creates a 2D array and accesses its properties:</p><pre><code class="language-c">// Create 2D array of float64 type
|
||
jl_value_t *array_type = jl_apply_array_type(jl_float64_type, 2);
|
||
jl_array_t *x = jl_alloc_array_2d(array_type, 10, 5);
|
||
|
||
// Get array pointer
|
||
double *p = (double*)jl_array_data(x);
|
||
// Get number of dimensions
|
||
int ndims = jl_array_ndims(x);
|
||
// Get the size of the i-th dim
|
||
size_t size0 = jl_array_dim(x,0);
|
||
size_t size1 = jl_array_dim(x,1);
|
||
|
||
// Fill array with data
|
||
for(size_t i=0; i<size1; i++)
|
||
for(size_t j=0; j<size0; j++)
|
||
p[j + size0*i] = i + j;</code></pre><p>Notice that while Julia arrays use 1-based indexing, the C API uses 0-based indexing (for example in calling <code>jl_array_dim</code>) in order to read as idiomatic C code.</p><h2><a class="nav-anchor" id="Exceptions-1" href="#Exceptions-1">Exceptions</a></h2><p>Julia code can throw exceptions. For example, consider:</p><pre><code class="language-c">jl_eval_string("this_function_does_not_exist()");</code></pre><p>This call will appear to do nothing. However, it is possible to check whether an exception was thrown:</p><pre><code class="language-c">if (jl_exception_occurred())
|
||
printf("%s \n", jl_typeof_str(jl_exception_occurred()));</code></pre><p>If you are using the Julia C API from a language that supports exceptions (e.g. Python, C#, C++), it makes sense to wrap each call into <code>libjulia</code> with a function that checks whether an exception was thrown, and then rethrows the exception in the host language.</p><h3><a class="nav-anchor" id="Throwing-Julia-Exceptions-1" href="#Throwing-Julia-Exceptions-1">Throwing Julia Exceptions</a></h3><p>When writing Julia callable functions, it might be necessary to validate arguments and throw exceptions to indicate errors. A typical type check looks like:</p><pre><code class="language-c">if (!jl_typeis(val, jl_float64_type)) {
|
||
jl_type_error(function_name, (jl_value_t*)jl_float64_type, val);
|
||
}</code></pre><p>General exceptions can be raised using the functions:</p><pre><code class="language-c">void jl_error(const char *str);
|
||
void jl_errorf(const char *fmt, ...);</code></pre><p><code>jl_error</code> takes a C string, and <code>jl_errorf</code> is called like <code>printf</code>:</p><pre><code class="language-c">jl_errorf("argument x = %d is too large", x);</code></pre><p>where in this example <code>x</code> is assumed to be an integer.</p><footer><hr/><a class="previous" href="environment-variables.html"><span class="direction">Previous</span><span class="title">Environment Variables</span></a><a class="next" href="packages.html"><span class="direction">Next</span><span class="title">Packages</span></a></footer></article></body></html>
|