доступ к переменным модуля fortran из С++

В настоящее время я работаю над проектом, который требует интеграции кода fortran в C++. В модуле fortran объявлено множество переменных и массивов. Я могу получить доступ к типам integer, float и double из c, объявив переменную c как extern double common_area_mp_rmax_, когда соответствующее объявление fortran — real*8 rmax, а имя модуля — common_area. Однако, когда я пытаюсь сделать то же самое для массива, я получаю сообщение об ошибке.

предположим, что код в модуле fortran: real*8,allocatable,dimension(:,:,:) :: x

Я создал двойной указатель c как:

extern "C" { double* common_area_mp_x_; }

Теперь, когда я компилирую весь проект, он говорит «множественное определение `variable_area_mp_x_'». Я использую CMake для компиляции всего проекта. Может кто-нибудь пролить свет, что я делаю неправильно? Я новичок в фортране, и мне становится трудно это исправить. Я ценю ваше время и помощь.

Спасибо, умник


person mindbender    schedule 22.01.2016    source источник
comment
нет, на самом деле у меня есть объявление на стороне C с extern. Я просто отредактирую и уменьшу эту путаницу.. спасибо..   -  person mindbender    schedule 22.01.2016
comment
Поскольку это всего лишь базовая переменная (а не структура, функция или класс), возможно, при необходимости вы могли бы исключить double * common_area_mp_x_ из файлов заголовков, а затем объявить extern double * common_area_mp_x_ в файле C++, где вы ссылаетесь на эту переменную.   -  person J.J. Hakala    schedule 22.01.2016


Ответы (2)


Fortran 2003 представил возможность взаимодействия с C в стандартном языке Fortran. Если у вас нет веских причин против этого, вам следует использовать возможности, предоставляемые этой языковой функцией. См. fortran -iso-c-binding на этом сайте для примера.

В соответствии с текущим стандартом Fortran (и в черновике следующей версии стандарта) выделяемая переменная модуля Fortran не может взаимодействовать с переменной C.

Что касается реализации, компилятор Фортрана будет использовать дескриптор для хранения статуса выделения выделяемой переменной. Этот дескриптор представляет собой нечто большее, чем просто указатель на данные — дополнительную информацию см. в разделе «Обработка дескрипторов массива Fortran» в руководстве пользователя и справочном руководстве по компилятору.

Наилучший подход к обмену информацией в этом случае зависит от того, что вы пытаетесь сделать. Один из вариантов — присвоить выделяемому массиву атрибут TARGET, а затем создать отдельную переменную TYPE(C_PTR) с меткой привязки с C-адресом цели. О таких аспектах, как размер массива, необходимо сообщать отдельно.

MODULE common_area
  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_DOUBLE
  IMPLICIT NONE
  ...
  REAL(C_DOUBLE), ALLOCATABLE, TARGET :: x(:,:,:)
  TYPE(C_PTR), BIND(C, NAME='x_ptr') :: x_ptr
CONTAINS
  ! Call before operating on x_ptr in C++
  SUBROUTINE init
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_LOC
    ALLOCATE(x(1,2,3))
    x_ptr = C_LOC(x)
  END SUBROUTINE init

~~~

// After init has been executed, this is a 
// pointer to the value of the allocatable module variable
extern "C" double *x_ptr;
person IanH    schedule 22.01.2016
comment
Согласно моему тесту, проблема, похоже, не на стороне фортрана, а в использовании extern "C" double * ptr, когда extern double * ptr должно было использоваться в коде C++. - person J.J. Hakala; 22.01.2016
comment
@ JJHakala J.J.Hakala Со стороны Фортрана BIND (C) для интероперабельных аргументов решает проблему. Есть и другие скрытые проблемы с подходом ОП. Это 2016 год... очень мало веских оправданий для того, чтобы не писать стандартный код для решения такого рода проблем. - person IanH; 22.01.2016
comment
ах, хорошо, теперь я более внимательно прочитал вики fortran-iso-c-binding. - person J.J. Hakala; 22.01.2016
comment
спасибо за полезные комментарии IanH и J.J. Халака, я пробовал использовать fortran-iso-c-binding для тестовой программы, и это сработало.. так что, думаю, теперь я изменю свой код, чтобы он заработал.. еще раз спасибо.. - person mindbender; 22.01.2016

Дан файл C++ test.cpp с содержимым

extern "C" { double * foo; }
extern double bar;
double asdf;

с помощью инструментов гну

g++ -Wall -c test.cpp; nm test.o
> 0000000000000000 B asdf
> 0000000000000008 B foo

то есть использование первого варианта фактически вводит новый символ с этим именем.

В этом случае правильным выбором будет

extern double * common_area_mp_x_;
person J.J. Hakala    schedule 22.01.2016