Passing Arrays to embedded Julia

I’m currently attempting to call Julia base functions from C# using the ‘julia.h’ and ‘libjulia.dll’ and I’m getting stuck when passing an array as a function argument. Maybe 1 of you can help?
I can call functions from base like sqrt() that takes an integer or float and get the result back. I’m also capable of writing an array to julia and reading it back. But when calling ‘jl_call1’ with a function pointer and an array pointer I get gibberish back. I have tried disabling the garbage collector with no effect.
Any ideas?

1 Like

jl_call1 takes jl_value_t* arguments. You can convert e.g. C double* pointer to a Julia object with jl_ptr_to_array_1d.

However, I would suggest writing all of the glue code on the Julia side, by writing Julia functions that take C-like arguments that do the conversion in Julia, then use cfunction to get C function pointers that you can call externally without using jl_call1 etc… e.g. you can write a function:

function do_stuff(n, a_::Ptr{Cdouble})
    A = unsafe_wrap(Array, a_, n)
    return sum(A) # or whatever
end

Then, from an embedding program in C (hopefully C# is similar?), do

double (*do_stuff)(size_t, double*) = jl_unbox_voidpointer(jl_eval_string("cfunction(do_stuff, Cdouble, (Csize_t, Ptr{Cdouble}))"));

to get an ordinary C function pointer that you can call.

2 Likes

Colleague of @McC0dy here.

jl_call1 is working just fine - e.g. with sort(4.0) being 2.0.
jl_ptr_to_array_1d is working just fine - we can create both uint8 and float64 arrays with variable number of elements.

What is not working for us is simple Base functions on arrays (like mean, reverse etc.), e.g. for mean (in pseudo-C syntax - we have yet to try C instead of C#):

jl_array_t *array_ptr = jl_ptr_to_array_1d(...)
jl_value_t *value_ptr = jl_call1(mean_func_ptr, array_ptr)
double *value = jl_unbox_float64(value_ptr)

It’s kind of unreasonable to ask people here to guess what is happening in your code. You’re more likely to get useful responses if you share a minimal example. (C# runnable under Mono has a better chance to get attention)

1 Like

Sorry if it seemed unreasonable - I did not expect someone to guess what is happening in our code, just asked in case somebody had experience with embedding julia in the case of calling functions with arrays as arguments (seemed like a plausible use case).

I just tried the C interface - and just the C interface - no C#.

The following (content of embed.c) runs just fine on macOS (El Capitan):

#include <julia.h>

int main(int argc, char *argv[])
{
    /* required: setup the Julia context */
    jl_init(NULL);
    
    /* run Julia commands */

    printf("--- High-Level Embedding ---\n");

    jl_eval_string("print(sqrt(2.0))");
    
    printf("\n--- Converting Types ---\n");
    
    jl_value_t *ret = jl_eval_string("sqrt(2.0)");
    
    if (jl_is_float64(ret)) {
        double ret_unboxed = jl_unbox_float64(ret);
        printf("sqrt(2.0) in C: %e \n", ret_unboxed);
    }
    
    printf("\n--- Calling Julia Functions ---\n");
    
    jl_function_t *func = jl_get_function(jl_base_module, "sqrt");
    jl_value_t *argument = jl_box_float64(4.0);
    ret = jl_call1(func, argument);
    if (jl_is_float64(ret)) {
        double ret_unboxed = jl_unbox_float64(ret);
        printf("sqrt(4.0) in C: %e \n", ret_unboxed);
    }
    
    printf("\n--- Working with Arrays ---\n");
    
    double *existingArray = (double*)malloc(sizeof(double)*10);
    jl_value_t* array_type = jl_apply_array_type(jl_float64_type, 1);
    jl_array_t *x = jl_ptr_to_array_1d(array_type, existingArray, 10, 0);
    double *xData = (double*)jl_array_data(x);
    
    for(size_t i=0; i<jl_array_len(x); i++)
        xData[i] = i;
    printf("x = [");
    for(size_t i=0; i<jl_array_len(x); i++)
            printf("%e ", xData[i]);
    printf("]\n");
    
    jl_function_t *reverse_func  = jl_get_function(jl_base_module, "reverse!");
    jl_call1(reverse_func, (jl_value_t*)x);
    printf("reverse(0...9) = [");
    for(size_t i=0; i<jl_array_len(x); i++)
            printf("%e ", xData[i]);
    printf("]\n");

    jl_function_t *mean_func  = jl_get_function(jl_base_module, "mean");
    ret = jl_call1(mean_func, (jl_value_t*)x);
    if (jl_is_float64(ret)) {
        double ret_unboxed = jl_unbox_float64(ret);
        printf("mean(0...9) in C: %e \n", ret_unboxed);
    }
    
    /* 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;
}

To build and execute:

export JULIA_DIR=/Applications/Julia-0.5.app/Contents/Resources/julia
$JULIA_DIR/share/julia/julia-config.jl --cflags --ldflags --ldlibs | xargs gcc embed.c -o embed
export JULIA_HOME=$JULIA_DIR/bin
./embed

jl_unbox_float64 returns double, not double*

Edit: your final example has the correct types, glad to hear it is working.

1 Like