Passing a Julia function to C and then calling from C code

Hello,

I am trying to pass a Julia function to a some C code and then call that Julia function from within my C code. I followed the instruction from the section https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#Creating-C-Compatible-Julia-Function-Pointers, but still when I try to call the Julia function in C, I got segmentation fault.

My Julia code is:

function mycompare(a)::Cint
    return 2 * a
end

function mycompare2(a, b)::Cint
    return (a < b) ? -1 : ((a > b) ? +1 : 0)
end

struct my_userdata_type_c
    n::Cint
    func::Ptr{Cvoid}
end

struct my_userdata_type_2_c
    p::Cdouble
end

function main()
    cd("/home/fah33/Test_C_Julia")
    A = [1.3, -2.7, 4.4, 3.1]
    @show A
    mycompare_c2 = @cfunction(mycompare2, Cint, (Ref{Cdouble},Ref{Cdouble}));
    ccall(:qsort, Cvoid, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Cvoid}),
             A, length(A), sizeof(eltype(A)), mycompare_c2)
    @show A
    A = [1.3, -2.7, 4.4, 3.1]
    @show A
    struct_test_1 = my_userdata_type_c(100, mycompare_c2)
    ccall(:qsort, Cvoid, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Cvoid}),
             A, length(A), sizeof(eltype(A)), struct_test_1.func)
    @show A
    mycompare_c = @cfunction(mycompare, Cint, (Ref{Cint},));
    @show ccall((:compare, "/home/fah33/Test_C_Julia/sampleProgram.so"), Int64, (Cint,), 4)
    @show ccall((:compare, "/home/fah33/Test_C_Julia/sampleProgram.so"), Int64, (Cint,), 3)
    #@show ccall((:eval_f, "/home/fah33/Test_C_Julia/sampleProgram.so"), Int64, (Cint, Ptr{Cvoid}), 1000, mycompare_c)
    struct_test_2 = my_userdata_type_2_c(3.0)
    @show ccall((:testFct2, "/home/fah33/Test_C_Julia/sampleProgram.so"), Int64, (Ref{my_userdata_type_2_c},), struct_test_2)
    @show struct_test_2.p
    struct_test = my_userdata_type_c(100, mycompare_c)
    @show struct_test
    #@show ccall((:testFct, "/home/fah33/Test_C_Julia/sampleProgram.so"), Int64, (my_userdata_type_c,), struct_test)
end

main()

My C code is:

#include<stdio.h>

int compare(int n){
    return n;
}

struct userdata_type {
    int n;
    int (*function)(int n);
};

int testFct(struct userdata_type user){
    printf("\n!testFct\n\n");
    printf("%d\n", user.n);
    return (*user.function)(user.n);
}

struct userdata_type2 {
    double p;
};

double testFct2(struct userdata_type2 user){
    printf("\ntestFct2\n\n");
    printf("%f\n", user.p);
    user.p = 2 * user.p;
    return user.p;
}

int function_test(int x){
    return x;
}

int eval_f(int n, int (*f)(int)){
    printf("\neval_f\n\n");
    printf("%d\n", (*f)(n));
    return 0;
}

int main()
{
    printf("\nA sample C program\n\n");
    struct userdata_type userdata;
    userdata.n = 1;
    userdata.function = compare;
    printf("%d\n", userdata.n);
    printf("%d\n", userdata.function(1));
    printf("%d\n", eval_f(20000, function_test));
    printf("\nA sample C program\n\n");
    return 0;
}

When I uncomment calling eval_f or testFct, I got Segmentation fault with an error stating unknown function.

The commands I use to run my code are:

gcc sampleProgram.c -o sampleProgram
gcc -shared -o sampleProgram.so -fPIC sampleProgram.c
julia sampleProgram.jl

Thank you for your help.

1 Like

You can think of Ref{Cint} as int *.

The following C and Julia code work together.

#include<stdio.h>

int compare(int *n){
    return *n;
}

struct userdata_type {
    int n;
    int (*function)(int * n);
};

int testFct(struct userdata_type user){
    printf("\n!testFct\n\n");
    printf("%d\n", user.n);
    return (*user.function)(&(user.n));
}

struct userdata_type2 {
    double p;
};

double testFct2(struct userdata_type2 user){
    printf("\ntestFct2\n\n");
    printf("%f\n", user.p);
    user.p = 2 * user.p;
    return user.p;
}

int function_test(int* x){
    return *x;
}

int eval_f(int n, int (*f)(int*)){
    printf("\neval_f\n\n");
    printf("%d\n", (*f)(&n));
    return 0;
}

int main()
{
    printf("\nA sample C program\n\n");
    struct userdata_type userdata;
    userdata.n = 1;
    userdata.function = compare;
    printf("%d\n", userdata.n);
    int x = 1;
    printf("%d\n", userdata.function(&x));
    printf("%d\n", eval_f(20000, function_test));
    printf("\nA sample C program\n\n");
    return 0;
}
function mycompare(a)::Cint
    return 2 * a
end

function mycompare2(a, b)::Cint
    return (a < b) ? -1 : ((a > b) ? +1 : 0)
end

struct my_userdata_type_c
    n::Cint
    func::Ptr{Cvoid}
end

struct my_userdata_type_2_c
    p::Cdouble
end

function main()
    #cd("/home/fah33/Test_C_Julia")
    A = [1.3, -2.7, 4.4, 3.1]
    @show A
    mycompare_c2 = @cfunction(mycompare2, Cint, (Ref{Cdouble},Ref{Cdouble}));
    ccall(:qsort, Cvoid, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Cvoid}),
             A, length(A), sizeof(eltype(A)), mycompare_c2)
    @show A
    A = [1.3, -2.7, 4.4, 3.1]
    @show A
    struct_test_1 = my_userdata_type_c(100, mycompare_c2)
    ccall(:qsort, Cvoid, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Cvoid}),
             A, length(A), sizeof(eltype(A)), struct_test_1.func)
    @show A
    mycompare_c = @cfunction(mycompare, Cint, (Ref{Cint},));
    @show ccall((:compare, "./sampleProgram.so"), Int64, (Ref{Cint},), 4)
    @show ccall((:compare, "./sampleProgram.so"), Int64, (Ref{Cint},), 3)
    @show ccall(mycompare_c, Cint, (Ref{Cint},), 10)
    @show ccall((:eval_f, "./sampleProgram.so"), Int64, (Cint, Ptr{Cvoid}), 1000, mycompare_c)
    struct_test_2 = my_userdata_type_2_c(3.0)
    @show ccall((:testFct2, "./sampleProgram.so"), Int64, (Ref{my_userdata_type_2_c},), struct_test_2)
    @show struct_test_2.p
    struct_test = my_userdata_type_c(100, mycompare_c)
    @show struct_test
    @show ccall((:testFct, "./sampleProgram.so"), Int64, (my_userdata_type_c,), struct_test)
end

main()

I get the following output:

$ julia sampleProgram.jl
A = [1.3, -2.7, 4.4, 3.1]
A = [-2.7, 1.3, 3.1, 4.4]
A = [1.3, -2.7, 4.4, 3.1]
A = [-2.7, 1.3, 3.1, 4.4]
ccall((:compare, "./sampleProgram.so"), Int64, (Ref{Cint},), 4) = 4
ccall((:compare, "./sampleProgram.so"), Int64, (Ref{Cint},), 3) = 3
ccall(mycompare_c, Cint, (Ref{Cint},), 10) = 20

eval_f

2000
ccall((:eval_f, "./sampleProgram.so"), Int64, (Cint, Ptr{Cvoid}), 1000, mycompare_c) = 0

testFct2

0.000000
ccall((:testFct2, "./sampleProgram.so"), Int64, (Ref{my_userdata_type_2_c},), struct_test_2) = 0
struct_test_2.p = 3.0
struct_test = my_userdata_type_c(100, Ptr{Nothing} @0x00007f8995ae9ee0)

!testFct

100
ccall((:testFct, "./sampleProgram.so"), Int64, (my_userdata_type_c,), struct_test) = 200
1 Like

Let’s say we kept your original C:

struct userdata_type3 {
    int n;
    int (*function)(int n);
};

int testFct3(struct userdata_type3 user){
    printf("\n!testFct3\n\n");
    printf("%d\n", user.n);
    return (*user.function)(user.n);
}

We can then alter the Julia code to match:

    mycompare_c_noref = @cfunction(mycompare, Cint, (Cint,))
    @show ccall(mycompare_c_noref, Cint, (Cint,), 5)
    struct_test2 = my_userdata_type_c(300, mycompare_c_noref)
    @show ccall((:testFct3, "./sampleProgram.so"), Cint, (my_userdata_type_c,), struct_test2)

I then get the following output:

ccall(mycompare_c_noref, Cint, (Cint,), 5) = 10

!testFct3

300
ccall((:testFct3, "./sampleProgram.so"), Cint, (my_userdata_type_c,), struct_test2) = 600

The confusing thing is that Julia’s @cfunction will automatically dereference Ref{Int} to Int when passing the value to a Julia function.

Thank you for the help and clarifying this.