Фортран принимает строку (?) От C - PullRequest
4 голосов
/ 10 апреля 2011

Я чувствую, что это должен быть простой вопрос, но я не могу заставить его работать. У меня есть некоторый код на Фортране, который принимает данные вроде:

      SUBROUTINE TRACE(X,Y,NAME,XX,YY)
      EXTERNAL NAME
      CALL NAME(X,Y,XX,YY)

и я пытаюсь передать имя из C ++ в виде:

float x,y,xx,yy;
char * name="IGRF";
trace_(&x,&y,name,&xx,&yy);

Он компилируется, но я всегда получаю ошибки segfaults, когда пытаюсь вызвать подпрограмму NAME. Подпрограмма с именем IGRF определена в файле, и я могу вызвать подпрограмму IGRF непосредственно из C ++, но нужна эта подпрограмма TRACE. При запуске в gdb он говорит, что переменная NAME указывается как void.

Я попытался передать NAME, & NAME & & NAME [0], символ NAME [4], у которого отсутствует \ 0, чтобы идеально соответствовать имени, и все они возвращаются с одним и тем же указателем void. Кто-нибудь знает, как получить имя функции из C ++ в эту переменную EXTERNAL в Fortran?

Спасибо

Ответы [ 4 ]

12 голосов
/ 10 апреля 2011

Таким образом, одно из преимуществ Fortran2003 и более поздних версий заключается в том, что совместимость с C определена в стандарте;это немного PITA для использования, но как только это будет сделано, он гарантированно будет работать на разных платформах и компиляторах.

Итак, вот cprogram.c, вызывая подпрограмму Fortran getstring:

#include <stdio.h>

int main(int argc, char **argv) {
    int l;
    char *name="IGRF";

    l = getstring(name);

    printf("In C: l = %d\n",l);

    return 0;
}

и вот fortranroutine.f90:

integer(kind=c_int) function getstring(instr) bind(C,name='getstring') 
    use, intrinsic :: iso_c_binding
    character(kind=c_char), dimension(*), intent(IN) :: instr
    integer :: len
    integer :: i

    len=0
    do
       if (instr(len+1) == C_NULL_CHAR) exit
       len = len + 1
    end do


    print *, 'In Fortran:'
    print *, 'Got string: ', (instr(i),i=1,len)
    getstring = len
end function getstring

Makefile достаточно прост:

CC=gcc
FC=gfortran

cprogram: cprogram.o fortranroutine.o
    $(CC) -o cprogram cprogram.o fortranroutine.o -lgfortran

fortranroutine.o: fortranroutine.f90
    $(FC) -c $^

clean:
    rm -f *.o cprogram *~

и работает, как в gcc / gfortran, так и в icc / ifort:

 In Fortran:
 Got string: IGRF
In C: l = 4

Обновление : О, я только что понял, что то, что вы делаете, гораздо сложнее, чем просто передача строки;Вы по сути пытаетесь передать указатель на функцию, указывающую на процедуру обратного вызова C.Это немного сложнее, потому что вы должны использовать Fortran interface s для объявления подпрограммы C - простое использование extern не сработает (и в любом случае не так хорошо, как явные интерфейсы, так как нет проверки типов и т. Д.)Так что это должно работать:

cprogram.c:

#include <stdio.h>

/* fortran routine prototype*/
int getstring(char *name, int (*)(int));

int square(int i) {
    printf("In C called from Fortran:, ");
    printf("%d squared is %d!\n",i,i*i);
    return i*i;
}


int cube(int i) {
    printf("In C called from Fortran:, ");
    printf("%d cubed is %d!\n",i,i*i*i);
    return i*i*i;
}

int main(int argc, char **argv) {
    int l;
    char *name="IGRF";

    l = getstring(name, &square);
    printf("In C: l = %d\n",l);
    l = getstring(name, &cube);
    printf("In C: l = %d\n",l);


    return 0;
}

froutine.f90:

integer(kind=c_int) function getstring(str,func) bind(C,name='getstring')
    use, intrinsic :: iso_c_binding
    implicit none
    character(kind=c_char), dimension(*), intent(in) :: str
    type(c_funptr), value :: func

    integer :: length
    integer :: i

    ! prototype for the C function; take a c_int, return a c_int
    interface
        integer (kind=c_int) function croutine(inint) bind(C)
            use, intrinsic :: iso_c_binding
            implicit none
            integer(kind=c_int), value :: inint
        end function croutine
    end interface
    procedure(croutine), pointer :: cfun

    integer(kind=c_int) :: clen

    ! convert C to fortran procedure pointer,
    ! that matches the prototype called "croutine"
    call c_f_procpointer(func, cfun)

    ! find string length
    length=0
    do
       if (str(length+1) == C_NULL_CHAR) exit
       length = length + 1
    end do

    print *, 'In Fortran, got string: ', (str(i),i=1,length), '(',length,').'

    print *, 'In Fortran, calling C function and passing length'
    clen = length
    getstring = cfun(clen)

end function getstring

И результаты:

$ gcc -g -Wall   -c -o cprogram.o cprogram.c
$ gfortran -c fortranroutine.f90 -g -Wall
$ gcc -o cprogram cprogram.o fortranroutine.o -lgfortran -g -Wall
$ gpc-f103n084-$ ./cprogram 
./cprogram 
 In Fortran, got string: IGRF(           4 ).
 In Fortran, calling C function and passing length
In C called from Fortran:, 4 squared is 16!
In C: l = 16
 In Fortran, got string: IGRF(           4 ).
 In Fortran, calling C function and passing length
In C called from Fortran:, 4 cubed is 64!
In C: l = 64
3 голосов
/ 10 апреля 2011

Похоже, что для FORTRAN 77 требуется указатель на символы и длина строки, передаваемой из C ++ (или C) в FORTRAN.

См. this utah.edu документ об использовании C и C ++ с FORTRAN и, в частности, поиск раздела документа, который начинается с "CHARACTER * n arguments".

2 голосов
/ 10 апреля 2011

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

То есть в вашем примере NAME должен быть указателем на функцию, а не строкой.Используя функцию ISO_C_BINDING, вы можете передавать указатели функций между C и Fortran.

0 голосов
/ 04 июля 2017

Я тоже работал с CMake.

CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)
project(CppFortran C CXX Fortran)

add_executable(CppFortran
    froutine.f90
    main.cpp
    )

main.cpp

#include <iostream>

extern "C" {
int getString(char *file_name);
}

int main() {
    int l;
    char *name = (char*)"IGRF";

    l = getString(name);
    std::cout << "In C++:"<< std::endl;
    std::cout << "length: " << l << std::endl;

    return 0;
}

froutine.f90

integer(kind=c_int) function getString(instr) bind(C,name='getString')
    use, intrinsic :: iso_c_binding
    character(kind=c_char), dimension(*), intent(IN) :: instr
    integer :: len
    integer :: i

    len=0
    do
        if (instr(len+1) == C_NULL_CHAR) exit
        len = len + 1
    end do

    print *, 'In Fortran:'
    print *, 'Got string: ', (instr(i),i=1,len)
    getstring = len
end function getString

Вывод на консоль:

 In Fortran:
 Got string: IGRF
In C++:
length: 4
...