Jak uzyskać dostęp (dynamicznie przydzielane) tablice Fortran w C
Moje główne pytanie brzmi, dlaczego tablice robią takie dziwne rzeczy i czy w ogóle istnieje sposób, aby zrobić to w „czysty” sposób.
Obecnie mam program w Cfoo.c
połączenie programu Fortranbar.f90
przezdlopen/dlsym
, mniej więcej tak jak w poniższym kodzie:
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
Uruchamianie głównych zbiorów
0.0000000000000000 3.1400000000000001 6.2800000000000002 9.4199999999999999
0.000000 -nan 0.000000 0.000000
który pokazuje, że Fortran poprawnie alokuje tablicę i nawet poprawnie zapisuje podane wartości, ale nie są one już dostępne przez dlsym (praca nad tymi danymi powoduje segfaults). Wypróbowałem to również dla tablic o stałych rozmiarach - wyniki pozostają takie same.
Czy ktoś zna powód takiego zachowania? Osobiście oczekiwałbym, że rzeczy albo będą działały dwukierunkowo, albo alternatywnie wcale - to „Fortran akceptujący tablice C, ale nie na odwrót” sprawia, że zastanawiam się, czy popełniłem jakiś podstawowy błąd w dostępie do tablicy z C w ten sposób.
Drugie (a nawet ważniejsze) pytanie brzmi: jak zrobić dostęp do tablicy w ten sposób „we właściwy sposób”. Obecnie nie jestem nawet pewien, czy trzymanie się interfejsu „Fortran jako .so” w ogóle jest dobrym sposobem - myślę, że w tym przypadku można byłoby również spróbować programowania mieszanego. Niemniej jednak problemy z tablicami pozostają - przeczytałem, że można to jakoś rozwiązać za pomocą ISO C Binding, ale nie mogłem się jeszcze dowiedzieć, jak to zrobić (jeszcze nie pracowałem zbyt dużo z Fortranem, zwłaszcza w przypadku Binding) , więc pomoc w tej sprawie byłaby bardzo mile widziana.
Edytować:
Dobra, więc trochę więcej czytam w ISO C Binding i znalazłem całkiem przydatne podejścietutaj. Za pomocąC_LOC
Mogę uzyskać wskaźniki C do moich struktur Fortran. Niestety, wskaźniki do tablic wydają się być wskaźnikami do wskaźników i należy je wyreferencjonować w kodzie C, zanim będą mogły być traktowane jako tablice C - lub coś takiego.
Edytować:
Teraz mój program działa, używając wiązania C w sposób, który wskazał Vladimir F, przynajmniej w większości. Pliki C i Fortran są teraz połączone ze sobą, więc mogę uniknąć interfejsu libdl, przynajmniej dla części Fortran - wciąż muszę załadować dynamiczną bibliotekę C, uzyskać wskaźnik funkcji do jednego z symboli i przekazać to jako wskaźnik funkcji do Fortrana, który później wywołuje tę funkcję w ramach obliczeń. Jak wspomniano funkcja oczekuje podwójnie * s [tablice], nie udało mi się przekazać moich tablic Fortrana za pomocą C_LOC, co dziwne - aniC_LOC(array)
aniC_LOC(array(1))
przekazał poprawne wskaźniki z powrotem do funkcji C.array(1)
zrobiłem sztuczkę. Niestety, nie jest to „najczystszy” sposób. Jeśli ktoś ma dla mnie podpowiedź, jak to zrobić, używającC_LOC
funkcja, która byłaby świetna. Niemniej jednak akceptuję odpowiedź Vladimira F, ponieważ uważam to za bezpieczniejsze rozwiązanie.