Как передать 2D-массив в C ++ в подпрограмму Fortran? - PullRequest
5 голосов
/ 03 ноября 2010

Я пишу небольшую программу на C ++, которая передает двумерный массив (комплексных чисел) в подпрограмму Fortran и получает ее обратно, заполненную значениями. Я написал версию, которая передает и получает 1-D массив, и это работает хорошо. Двухмерная версия не работает (моя настоящая цель - написать четырехмерную версию с большими размерами, поэтому эти массивы нужно распределять динамически).

Я опубликую и мой рабочий код, и нерабочий код, но сначала обратите внимание, что я был вынужден использовать структуры (простые, содержащие только два типа double), потому что Fortran, похоже, интерпретирует их точно так же, как и свой собственный сложные числа. Вот почему моя 1-D версия работает. По той же причине, я не думаю, что это проблема «комплексного числа».

Вот мой рабочий код. Передача 1-D массива комплексных чисел в подпрограмму Fortran:

Подпрограмма Fortran:

subroutine carray(A)
complex*16 A(2)

A(1) = cmplx(3,7)
A(2) = cmplx(9,5)

return
end

Код C ++:

include <iostream>
include <complex>
using namespace std;

struct cpx{double r, i;};

extern"C"
{
   void carray_(struct cpx* A);
}

int main()
{
   struct cpx* A;
   A = new struct cpx [2];

   carray_(A);

   complex<double>* P;
   P = new complex<double> [2];

   for(x = 0; x < 2; x++)
   {
      real(P[x] = A[x].r;
      imag(P[x] = A[x].i;
   }

   cout << real(P[0]) << "\t" << imag(P[0]) << "\n";
   cout << real(P[1]) << "\t" << imag(P[1]) << "\n";

   return 0;
}

Компиляция с помощью следующих команд работает без жалоб:

gfortran -c CarrayF.f
g++ -c CarrayC.cc
g++ -o Carray CarrayC.o CarrayF.o -lgfortran

Так что, пока я рассматриваю (нативное) комплексное число Фортрана как структуру из двух двойных чисел, я могу помещать их в (не нативный) комплексный тип C ++. Подпрограмма на Фортране, кажется, совершенно счастлива, получив указатель, где она ожидает массив. Пока все хорошо.

Вот моя нерабочая попытка передать массив 2D:

Код Фортрана:

subroutine carray(A)
complex*16 A(2,2)

A(1,1) = cmplx(3,7)
A(1,2) = cmplx(9,5)
A(2,1) = cmplx(2,3)
A(2,2) = cmplx(4,9)

return
end

Код C ++:

include <iostream>
include <complex>
using namespace std;

struct cpx{double r, i;};

extern"C"
{
   void carray_(struct cpx** A);
}

int main()
{
   struct cpx** A;
   A = new struct cpx* [2];
   for(int x = 0; x < 2; x++)
   {
      A[x] = new struct cpx [2];
   }

   carray_(A);

   complex<double>** P;
   P = new complex<double>* [2];
   for(int x = 0; x < 2; x++)
   {
      P[x] = new complex<double> [2];
   }

   for(x = 0; x < 2; x++)
   {
      for(int y = 0; y < 2; y++)
      {
         real(P[x][y] = A[x][y].r;
         imag(P[x][y] = A[x][y].i;
      }
   }

   cout << real(P[0][0]) << "\t" << imag(P[0][0]) << "\n";
   cout << real(P[0][1]) << "\t" << imag(P[0][1]) << "\n";
   cout << real(P[1][0]) << "\t" << imag(P[1][0]) << "\n";
   cout << real(P[1][1]) << "\t" << imag(P[1][1]) << "\n";

   return 0;
}

Это на самом деле компилируется без жалоб (та же процедура компиляции, что и для 1-D версии), но запуск исполняемого файла приводит к немедленной ошибке сегментации. Из-за головной боли от использования двух языков отладчик не помогает.

Я где-то допустил ошибку? Кажется, я не превышаю границ массива. Подпрограмма Fortran рада получить указатель, но, очевидно, она не понимает, что делать с указателем на указатель. Обычно, Fortran будет просто иметь дело с именами массивов, даже для многомерных массивов, но мне нужно понять, как Fortran работает с 2D-массивами.

Ответы [ 3 ]

8 голосов
/ 03 ноября 2010

Способ сделать это в эту эпоху - использовать привязку ISO C.Официально это часть Fortran 2003, но широко поддерживается компиляторами Fortran, включая gfortran.Это даже включает совместимость сложных типов!См. Список типов в руководстве по gfortran в главе «Внутренние модули», раздел «ISO_C_Binding».Плюс к этому есть несколько примеров в главе «Программирование на разных языках».На стороне C ++ используйте extern C для вызываемой подпрограммы, чтобы использовать соглашения о вызовах C без указания имени.На стороне Фортрана используйте привязку ISO C.Тогда у вас будет совместимость на уровне языка, а не делать предположения о том, как работают компиляторы.

6 голосов
/ 03 ноября 2010

Многомерные массивы в Fortran хранятся в виде плоских основных столбцов массивов в памяти. Они являются , а не указателями на указатели - на самом деле это просто одномерные массивы, где вам нужно выполнить дополнительную арифметику для вычисления индексов массива.

Правильный способ передачи массива из C ++ выглядит так:

extern "C" void carray_(struct cpx *A);
...
struct cpx A[2*2];
carray_(A);

complex<double> P[2][2];
for(int i = 0; i < 2; i++)
{
    for(int j = 0; j < 2; j++)
    {
        // note order of indicies: A is column-major
        P[i][j] = std::complex<double>(A[j*2 + i].r, A[j*2 + i].i);
    }
}
2 голосов
/ 03 ноября 2010

Новый совет

Двумерный массив в c ++ - это , а не - повторяю , а не - то же самое, что указатель на указатель {*}!

Когда вы делаете

struct cpx** A;

, вы настраиваете для создания так называемого «рваного массива», для которого нет эквивалента фортрана.Вы хотите что-то вроде

struct cpx *A[2][2] = new struct cpx[2][2];

, которое является указателем на двумерный массив с длиной строк 2.

{*} Да, вы можете получить доступ к структуре указатель-указатель, используяобозначения двумерного массива, но они по-разному расположены в памяти. :: grumble :: Люди, которые говорят другим людям, что массивы и указатели - это то же самое, что им нужно, чтобы встретить Большую пенную подсказку.

  • Двумерный массив - эторазовое выделение <row-dimension>*<column-dimension>*sizeof(<type>).Его имя превращается в указатель на тип.
  • Разорванный массив - это выделение 1+.Один - <column-dimension<*sizeof(<type>*), а другой - <row-dimension>*sizeof(<type>).

Старый, правильный, но неприменимый совет

Здесь следует помнить, что c ++ и fortranполагаем, что массивы хранятся по-разному в памяти.

C ++ считает, что позиция памяти после a[1][1] равна a[1][2], в то время как Фортран считает, что это a[2][1].Различие проводится между мажорной строкой (c, c ++ и т. Д.) И мажорной колонкой (fortran, matlab и некоторыми другими). ​​

Обратите внимание, что по умолчанию это отдельно от массивов индексации fortran из 1.

...