Мой главный вопрос заключается в том, почему массивы делают такие странные вещи и есть ли вообще способ сделать следующее «чистым» способом.
В настоящее время у меня есть программа C foo.c
, взаимодействующая с программой Fortran bar.f90
через dlopen/dlsym
, примерно как в приведенном ниже коде:
foo.c:
#include <dlfcn.h>
#include <stdio.h>
int main()
{
int i, k = 4;
double arr[k];
char * e;
void * bar = dlopen("Code/Test/bar.so", RTLD_NOW | RTLD_LOCAL);
void (*allocArray)(int*);
*(void **)(&allocArray) = dlsym(bar, "__bar_MOD_allocarray");
void (*fillArray)(double*);
*(void **)(&fillArray) = dlsym(bar, "__bar_MOD_fillarray");
void (*printArray)(void);
*(void **)(&printArray) = dlsym(bar, "__bar_MOD_printarray");
double *a = (double*)dlsym(bar, "__bar_MOD_a");
for(i = 0; i < k; i++)
arr[i] = i * 3.14;
(*allocArray)(&k);
(*fillArray)(arr);
(*printArray)();
for(i = 0; i < 4; i++)
printf("%f ", a[i]);
printf("\n");
return 0;
}
bar.f90:
module bar
integer, parameter :: pa = selected_real_kind(15, 307)
real(pa), dimension(:), allocatable :: a
integer :: as
contains
subroutine allocArray(asize)
integer, intent(in) :: asize
as = asize
allocate(a(asize))
return
end subroutine
subroutine fillArray(values)
real(pa), dimension(as), intent(in) :: values
a = values
return
end subroutine
subroutine printArray()
write(*,*) a
return
end subroutine
end module
Выполнение основных урожаев
0.0000000000000000 3.1400000000000001 6.2800000000000002 9.4199999999999999
0.000000 -nan 0.000000 0.000000
который показывает, что Fortran правильно выделяет массив и даже правильно сохраняет заданные значения, но они больше не доступны через dlsym (работа с этими данными приводит к segfaults). Я также пробовал это для массивов фиксированного размера - результаты остаются прежними.
Кто-нибудь знает причину такого поведения? Лично я ожидал, что все будет работать двунаправленно или, наоборот, не будет работать вообще - этот «Fortran принимает массивы C, но не наоборот» заставляет меня задуматься, есть ли какая-то основная ошибка, которую я сделал при доступе к массиву из C таким образом.
Другой (и даже более важный) вопрос заключается в том, как сделать такие обращения к массиву "правильным способом". В настоящее время я даже не уверен, что придерживаться интерфейса "Fortran as .so" - это вообще хороший способ - я думаю, что в этом случае также можно было бы попробовать смешанное программирование. Тем не менее, проблема с массивами остается - я читал, что это можно как-то решить с помощью привязки ISO C, но я пока не мог понять, как это сделать (я пока мало работал с Fortran, особенно с упомянутой привязкой) , так что помощь в этом вопросе будет принята с благодарностью.
Изменить:
Хорошо, поэтому я немного больше прочитал привязку ISO C и нашел довольно полезный подход здесь. Используя C_LOC
, я могу получить указатели C на мои структуры Fortran. К сожалению, указатели на массивы кажутся указателями на указатели и должны быть разыменованы в коде C, прежде чем их можно будет рассматривать как массивы C или что-то в этом роде.
Изменить:
Теперь моя программа работает, используя привязку C, как указал Владимир Ф, по крайней мере, по большей части. Файл C и файлы Fortran теперь связаны друг с другом, поэтому я могу избежать интерфейса libdl, по крайней мере, для части Fortran - мне все еще нужно загрузить динамическую библиотеку C, получить указатель функции на один из символов там и передать это как указатель функции на Fortran, который позже вызывает эту функцию как часть своих вычислений. Поскольку указанная функция ожидает двойные * s [массивы], мне не удалось передать мои массивы Fortran с помощью C_LOC, как ни странно, ни C_LOC(array)
, ни C_LOC(array(1))
не передали правильные указатели обратно в функцию C. Однако array(1)
сделал свое дело. К сожалению, это не самый «чистый» способ сделать это. Если бы кто-нибудь подсказал мне, как это сделать с помощью функции C_LOC
, было бы здорово. Тем не менее я принимаю ответ Владимира Ф., так как считаю его более безопасным решением.