Вызов функции C из Julia и передача 2D-массива в качестве указателя указателей в качестве аргумента

Фон

Я пытаюсь использовать функцию ccall Julia для использования кода, написанного на C. Я знаю, как передать массив в качестве аргумента функции, ожидающей int *arg . Например, пытаясь использовать эту функцию C

 void sum_one(int *arr, int len) { for (int i=0; i<len; i++){ arr[i]++; } } 

этот код Джулии работает

 x = collect(Cint, 1:5) ccall((:sum_one, "/path/to/mylib.so"), Void, (Ptr{Cint}, Cint), x, 5) 

Эта проблема

Кажется, это не так прямолинейно с функциями C, которые ожидают, что указатель на указатель ( int **arg ) будет использоваться как двумерная matrix. Скажите это

 void fill_matrix(int **arr, int row, int col) { for (int i=0; i<row; i++){ for (int j=0; j<col; j++){ arr[i][j] = arr[i][j] + i + j*10; } } } 

Здесь мне нужно было создать массив массивов, чтобы код C принял его:

 xx = [zeros(Cint, 5) for i in 1:6] ccall((:fill_matrix, "/path/to/mylib.so"), Void, (Ptr{Ptr{Cint}}, Cint, Cint), xx, 6,5) 

Но эта структура структуры не очень удобна со стороны Джулии.

Вопросы)

  • Есть ли другой способ передать двумерную матрицу функции C, которая ожидает аргумент типа int **arg ?
  • Если нет, как вы можете преобразовать уже существующий двумерный массив Julia в массив массивов структуры C?
  • и наоборот?

    Я постараюсь ответить вам на вопросы один за другим:

    Есть ли другой способ передать двумерную матрицу функции C, которая ожидает аргумент типа int ** arg?

    Да. Вы должны добавить метод в функцию cconvert julia, чтобы он выполнял преобразование из Matrix{Cint} в Ptr{Ptr{Cint}} . Итак, вы определяете:

     Base.cconvert(::Type{Ptr{Ptr{Cint}}},xx2::Matrix{Cint})=Ref{Ptr{Cint}}([Ref(xx2,i) for i=1:size(xx2,1):length(xx2)]) 

    (см. следующий вопрос для объяснения), и после этого вы можете напрямую передать свою матрицу в ccall:

     xx2=zeros(Cint,5,6) ccall((:fill_matrix, "mylib.so"),Void, (Ptr{Ptr{Cint}}, Cint, Cint), xx2, 6,5) 

    Тем не менее, я бы предложил очень консервативно использовать методы cconvert, которые вы перезаписываете, потому что другой код julia может ожидать оригинальное поведение.

    Если нет, как вы можете преобразовать уже существующий двумерный массив Julia в массив массивов структуры C?

    Должно работать следующее: вы создаете массив указателей для каждого столбца вашей матрицы, поэтому в julia-0.4:

     xx2=zeros(Cint,5,6) refAr=[Ref(xx2,i) for i=1:size(xx2,1):length(xx2)] ccall((:fill_matrix, "mylib.so"),Void, (Ptr{Ptr{Cint}}, Cint, Cint), refAr, 6,5) 

    Теперь matrix xx2 заполнена функцией C. Заметим, что в julia v0.3 вам нужно заменить Ref(xx2,i) на pointer(xx2,i)

    и наоборот?

    Я не думаю, что это вообще возможно. Чтобы построить 2D-массив julia, данные должны быть в непрерывном блоке памяти. Если вы ДЕЙСТВИТЕЛЬНО уверены, что это так, вы можете сделать:

     p=pointer(refAr) # This is a Ptr{Ptr{Cint}} representing the int** aa=pointer_to_array(p,6,false) bb=pointer_to_array(aa[1],(5,6),false) 

    Что возвращает исходную матрицу. Здесь последний аргумент pointer_to_array определяет, будет ли Julia владеть массивом, а данные должны быть освобождены gc Julia.

    Я не в Джулию, но C определенно позволяет вам передавать многомерные массивы:

     void fill_matrix(int row, int col, int (*arr)[col]) { for (int i=0; i 

    Дело в том, что не задействован массив указателей, выражение arr[i][j] будет оцениваться как arr[i*col + j] в силу типа указателя массива, используемого для объявления arr . Данные просто смежны в памяти.

    Теперь я не знаю, позволяет ли Julia взаимодействовать с C-функцией, которая принимает аргумент указателя массива, подобный этому, но вы можете попытаться выяснить это. Также может потребоваться обмен индексами массива, который зависит от того, сохраняет ли Julia свои матрицы в первом или первом порядке строк. В любом случае, это должно быть легко узнать, пытаясь.