Наберите marshalling для вызова подпрограммы фортрана из C # - PullRequest
2 голосов
/ 05 августа 2011

Я пытаюсь вызвать подпрограмму FORTRAN77 из кода C # с помощью P / invoke - на случай, если вам интересно, я пытаюсь обернуть некоторые из функций, предлагаемых библиотекой ARPACK (http://www.caam.rice.edu/software/ARPACK). I есть 2 вопроса.


Во-первых, я нигде не смог найти четких инструкций относительно сортировки типов в этом контексте. Более конкретно, вот типы, которые объявлены в моей подпрограмме FORTRAN:

       subroutine getEigenVectors
      &   ( Matrix, n, which, nev, ncv, maxn, maxnev, maxncv, ldv, v, d)

 c     %------------------%
 c     | Scalar Arguments |
 c     %------------------%

       character        which*2
       integer          n, nev, maxn, maxnev, maxncv, ldv

 c     %-----------------%
 c     | Array Arguments |
 c     %-----------------%
 c
       Real           
      &                 Matrix(n,n), v(ldv,maxncv), d(maxncv,2)

Я нашел здесь ценную информацию: Что я должен сделать MarshalAs для Типа персонажа в Фортране? , из чего я предположил (я могу ошибаться), что я должен использовать:

  • [MarshalAs(UnmanagedType.I4)] int для передачи целых чисел
  • [MarshalAs(UnmanagedType.LPArray)] byte[] для передачи в символьных строках

Однако я абсолютно не представляю, что делать с массивами Real. У кого-нибудь есть идеи по этому поводу?


Во-вторых, я не понимаю, должен ли я передавать свои аргументы в качестве ссылки или нет. Я никоим образом не знаком с Фортраном - я знаю, что это немного усложняет задачу; однако, только ARPACK делает то, что я хотел бы сделать. Я где-то читал, хотя подпрограммы FORTRAN по умолчанию принимают все свои аргументы в качестве ссылки. Должен ли я передавать все аргументы в качестве ссылок?

Заранее спасибо за помощь! Гийом


РЕДАКТИРОВАТЬ (8/6/11)

Итак, вот мой последний дубль:

    [DllImport("Arpack.dll", EntryPoint = "#140")]
    private static extern void getEigenVectors(
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] matrix, 
        [MarshalAs(UnmanagedType.I4)] ref int n,
        [MarshalAs(UnmanagedType.LPArray)] ref byte[] which,
        [MarshalAs(UnmanagedType.I4)] int whichLength,
        [MarshalAs(UnmanagedType.I4)] ref int nev, 
        [MarshalAs(UnmanagedType.I4)] ref int ncv,
        [MarshalAs(UnmanagedType.I4)] ref int maxn,
        [MarshalAs(UnmanagedType.I4)] ref int maxnev,
        [MarshalAs(UnmanagedType.I4)] ref int maxncv,
        [MarshalAs(UnmanagedType.I4)] ref int ldv,
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] v,
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] d
    );

Несколько вещей, которые я сделал здесь:

  • Передача int объектов, маршалированных как UnmanagedType.I4 для соответствия FORTRAN integer объектов
  • Передайте float[,] объекты размером (m, n) и маршалируйте как UnmanagedType.LPArray, чтобы соответствовать FORTRAN Real(n,m) объектам
  • Передача byte[] объектов, выделенных как UnmanagedType.LPArray для соответствия объектам FORTRAN Character*n. byte[] объекты вычисляются следующим образом:

    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    byte[] which = encoding.GetBytes(myString);
    
  • Передайте int объект BY VALUE и маршалинг как UnmanagedType.I4, чтобы указать длину строки. Обратите внимание, что я попытался поместить этот аргумент сразу после строки, а также в конце списка аргументов.

Это мой лучший выстрел - ни это, ни все другие вещи, которые я пробовал, не сработали. Метод будет выполнен, однако он выйдет очень быстро (когда он должен делать довольно серьезные вычисления). Более того, мои 3 массива превращаются в странные одномерные массивы. Вот что дает мне отладчик:

enter image description here

Есть идеи?

Ответы [ 2 ]

2 голосов
/ 07 августа 2011

Я предлагаю вам начать с небольшого тестового кода. Скомпилируйте FORTRAN .dll с некоторыми подпрограммами с простыми параметрами и поэкспериментируйте с C # , чтобы заставить вызов работать. Также вы можете заключить в Фортран множество аргументов в одну структуру (ключевое слово TYPE), что значительно упрощает взаимодействие.

Вот рабочий пример, который вы можете использовать, чтобы получить множество идей о том, как это работает.

Оригинальный код Фортрана:

  SUBROUTINE CALC2(BLKL,BLKW, N_LAMINA,N_SLICE, LOAD, SLOP,SKW,    &
                    DIA1,DIA2, Y1, Y2, N1, N2, DROP1, DROP2,        &
                    PARRAY, P_MAX, P_MAX_INDEX, ENDEFCT)
  !DEC$ ATTRIBUTES DLLEXPORT :: CALC2
  !DEC$ ATTRIBUTES ALIAS:'CALC2' :: CALC2
  !DEC$ ATTRIBUTES VALUE :: BLKL, BLKW, N_LAMINA, N_SLICE, LOAD, SLOP, SKW
  !DEC$ ATTRIBUTES VALUE :: DIA1, DIA2, Y1, Y2, N1, N2
  IMPLICIT NONE
  INTEGER*4, INTENT(IN) ::N_LAMINA, N_SLICE
  REAL*4, INTENT(IN) :: BLKL, BLKW, LOAD, SLOP, SKW,     &
                        DIA1, DIA2, Y1, Y2, N1, N2,   &
                        DROP1(MAX_LAMINA), DROP2(MAX_LAMINA)
  REAL*4, INTENT(OUT):: PARRAY(MAX_PATCH), P_MAX
  INTEGER*4, INTENT(OUT) :: P_MAX_INDEX, ENDEFCT
  INTEGER*4 :: NDIAG, N_PATCH
  REAL*4 :: SLNG, SWID
  REAL*4 :: DROPS_1(MAX_LAMINA), DROPS_2(MAX_LAMINA)

...

  END SUBROUTINE CALC2

с различными значениями скаляров и массивов в вещественной и целочисленной форме. Например, DROP1 - это входной одномерный массив. PARRAY выводит двумерный массив как одномерный массив. BLKL являются входными числами.

Обратите внимание на украшение !DEC$ ATTRIBUTES VALUE, чтобы не объявлять все как ref.

В C # код вызывается

    [DllImport("mathlib.dll")]
    static extern void CALC2(float major_dim, float minor_dim, 
        int N_lamina, int N_slices, float total_load, 
        float slope, float skew, float diameter_1, float diameter_2, 
        float youngs_1, float youngs_2, float nu_1, float nu_2, 
        float[] drops_1, float[] drops_2, float[] pressures, 
        ref float p_max, ref int p_max_index, ref EndEffect end_effect);

...
   {
        float max_pressure = 0;
        int max_pressure_index = 0;
        float[] pressures = new float[Definition.MAX_PATCH];
        EndEffect end_effect = EndEffect.NO;

        CALC2(length, width, lamina_count, slice_count, load, slope, skew, 
            dia_1, dia_2, y_1, y_2, n_1, n_2, drops_1, drops_2, pressures, 
            ref max_pressure, ref max_pressure_index, ref end_effect);
    }

note Я не пропускаю никаких строк.

0 голосов
/ 05 августа 2011

1) Цитирование Википедия :

Одиночная точность, называемая "float" в семействе языков C, и "real" или "real * 4" вFortran .Это двоичный формат, который занимает 32 бита (4 байта) и имеет значение и имеет точность 24 бита (около 7 десятичных цифр).

Так что распределяйте его как число с плавающей запятой.Вы могли бы проверить это, это либо число с плавающей запятой, либо двойное число.

2) Цитирование Учебник по Fortran 77 :

В Fortran 77 используется такназывается парадигмой вызова по ссылке.Это означает, что вместо простой передачи значений аргументов функции / подпрограммы (по значению) вместо этого передается адрес памяти аргументов (указателей).

Передача каждого параметра по ссылке.

...