Хобрук: Ваш путь к мастерству в программировании

Передавать массивы из C / C ++ в Fortran и возвращать вычисленный массив

Я пытаюсь передать массив из C / C ++ в модуль Fortran 2003 и вернуть вычисленные значения обратно в C / C ++. Мне удавалось передавать и возвращать одиночные значения (скаляры) очень хорошо, но получить массив туда и обратно оказалось затруднительным. Я нашел много потоков со скалярными значениями, и мне удалось заставить их работать.

Я смоделировал свои функции на основе массива по образцу моих рабочих скалярных функций.

Я использую gcc / gfortran.

Вот модуль Fortran (ConvertUnitsLib.f03).

module ConvertUnitsLib

use :: iso_c_binding ! for C/C++ interop
real(c_double), bind(c) :: degF, degC

public DegCtoF

contains

!
! Convert temperature degrees Celsius Fahrenheit
!
real(kind = c_double) function DegCtoF(degC) result(degF) &
    & bind(c, name = "DegCtoF")

    real(c_double), intent(in), dimension(:) :: degC
    real(c_double), dimension(size(degC)) :: degF

    do i = 1, size(degC)
        degF(i) = ( degC(i) * 1.8 ) + 32
    end do

end function DegCtoF


! End of module
end module ConvertUnitsLib

И C / C ++, (CFort.cpp)

#include <stdio.h>

#ifdef __cplusplus
extern"C" {
#endif
    double DegCtoF(double *[]);
#ifdef __cplusplus
}
#endif


/**********************************************************************/


int main(int argc, char *argv[])
{
    printf("C/C++ and Fortran together!\n");

    double DegreesC[2] = {32, 64};
    double DegreesF[2];

    DegreesF = DegCtoF(&DegreesC);
    printf("%3.1f [C] = %3.1f [F]\n", DegreesC, DegreesF );

    return 0;
}

И последнее, но не менее важное: Makefile

# C++ directives
CC=g++
CFLAGS=-std=c++11

# Fortran directives
FC=gfortran
FFLAGS=-std=f2003

all: clean
    $(FC) $(FFLAGS) -c -fcheck=all ConvertUnitsLib.f03
    $(CC) $(CFLAGS) -c CFort.cpp
    $(FC) $(FFLAGS) ConvertUnitsLib.o CFort.o -o convert

clean:
    rm -f *.o
    rm -f *.mod

  • Что ж, я не уверен насчет Фортрана, но в C и C ++ при передаче массива (который фактически передает адрес первого элемента) теряется вся информация о размере, поэтому ваша процедура Fortran ничего не знает о фактическом количестве элементов, содержащихся в массиве. . Я ожидал бы двух параметров: массива (или указателя на буфер) и количества элементов. 02.01.2016
  • Судя по вашим тегам, я предполагаю, что вы используете gfortran / gcc, но можете ли вы подтвердить? Передача аргумента предполагаемой формы, такого как degC, не является частью совместимости Fortran 2003 C и требует гораздо больше усилий. Аспект, который в настоящее время ожидается в F2015 (ISO TS), не поддерживается gcc. При отсутствии такой поддержки используйте комментарий @PaulMcKenzie и используйте явный массив форм с также переданным размером. 02.01.2016
  • @francescalus, да я использую gfortran / gcc. Когда вы имеете в виду больше усилий, нужно ли мне также передавать размер массива? Я знаю, что когда C вызывает функцию Fortran, он передает скалярные значения указателем. Мне просто нужно передать размер, а затем он (Фортран) сможет выяснить остальное? 02.01.2016
  • @francescalus, в этом есть смысл. Сейчас я работаю над этим кодом, но как мне передать его обратно из Фортрана в C? 02.01.2016
  • @francescalus, единственный способ заставить скалярные переменные перемещаться между языками - это объявить их общедоступными. 02.01.2016
  • Спасибо @francescalus за помощь! +1 за объяснения происходящего. 02.01.2016
  • Есть также много предыдущих ответов об использовании ISO C BInding для передачи массивов, например, stackoverflow.com/questions/28697754/, stackoverflow.com/questions/28003625/, stackoverflow.com/questions/26678961/, stackoverflow.com / questions / 22310734 / 02.01.2016
  • Об этом не стоит упоминать в моем ответе, но ваша инструкция функции Fortran неверна: если вы объявляете тип результата (real(c_double)), вы также не можете объявить тип результата в более поздней строке. См., Например, stackoverflow.com/q/24170024. 02.01.2016
  • Нет языков C / C ++. Это похоже на обычный C, поэтому вам следует удалить тег C ++. 02.01.2016

Ответы:


1

Прежде чем francescalus подтвердит это, я собирался сказать, что из того, что я знаю, это было немного старовато, совместимость не позволяет то, что вы пытаетесь делать с массивами. Кроме того, при кодировании всегда решающее значение имеют хорошие привычки. Например, использование implicit none в fortran для принудительного объявления всех переменных перед их использованием. Использование именованной константы, если это позволяет язык, например 2, который вы используете в качестве размера массива в fortran.

Ниже приведена измененная версия вашего кода, которая должна делать что-то вроде того, чего вы хотите достичь.

// Фортран

module ConvertUnitsLib

use :: iso_c_binding ! for C/C++ interop
!real(c_double), bind(c) :: degF, degC
implicit none

public DegCtoF

contains

!
! Convert temperature degrees Celsius Fahrenheit
!
subroutine DegCtoF(degC, degF, n)&
    bind(c, name = "DegCtoF")

    integer, intent(in) :: n
    real(c_double), intent(in), dimension(n) :: degC
    real(c_double), intent(out), dimension(n) :: degF
    integer :: i

    do i = 1, n
        degF(i) = ( degC(i) * 1.8 ) + 32
    end do

end subroutine DegCtoF

// C++

#include <stdio.h>

#ifdef __cplusplus
extern"C" {
    #endif
    double DegCtoF(double [], double [], const int *);
    #ifdef __cplusplus
}
#endif


/**********************************************************************/


int main(int argc, char *argv[])
{
    const int N = 2;
    printf("C/C++ and Fortran together!\n");

    double DegreesC[N] = {32, 64};
    double DegreesF[N];

    DegCtoF(DegreesC, DegreesF, &N);
    for(int i = 0; i<N; i++){
        printf("%d : %3.1f [C] = %3.1f [F]\n", i, DegreesC[i], DegreesF[i] );
    }

    return 0;
}
02.01.2016
  • Это то, что я ищу. Просто протестировал код, и он отлично работает. Но в чем я не уверен, так это в безопасности использования подпрограммы. Исходя из IDL, подпрограммы позволяют блоку кода изменять любую внутреннюю переменную в блоке. По сути, подпрограмма выполняется в оперативном режиме. В Фортране то же самое? Что мне любопытно, так это то, что если я скажу переменную с именем NUM в подпрограмме, сможет ли подпрограмма изменить эту переменную в глобальном смысле? 02.01.2016
  • Я забыл упомянуть о том, чтобы превратить его в подпрограмму. Вот как я это делал несколько лет назад. Спасибо @francescalus за упоминание об этом в своем ответе. Я ничего не знаю о IDL. Что касается встроенного исполнения, я не понимаю вашего вопроса. 02.01.2016

  • 2

    В соответствии с правилами текущего Фортрана (Фортран 2008, но это то же самое, когда возможность взаимодействия с C была введена в Фортран 2003), процедура Фортрана не совместима с С, если она имеет фиктивный аргумент предполагаемой формы (также применяются другие ограничения). В вашем коде degC фиктивный аргумент в функции DegCtoF, объявленный как

    real(c_double), intent(in), dimension(:) :: degC
    

    такое дело.

    Итак, в F2003 у вас не может быть такой функционально совместимой функции. Здесь все становится непросто.

    В предлагаемом проекте для F2015 (на основе ISO TS29113 Дополнительная совместимость Fortran с C) такая вещь совместима. И этот синтаксис (я думаю) поддерживается последними версиями gcc, поэтому код не отклоняется gfortran.

    (TS) Однако стандартизованное взаимодействие с такой процедурой с предполагаемым аргументом формы требует использования дескриптора C, описанного в ISO_Fortran_binding.h, на стороне C, что не реализовано в gcc. Вместо этого для такого взаимодействия требуется непосредственное понимание дескриптора массива gcc.

    Но тебе повезло. В вашем случае вам действительно не нужно использовать фиктивный аргумент предполагаемой формы: вы можете использовать явный фиктивный аргумент формы, и такое взаимодействие является частью F2003. Все, что вам нужно сделать, это передать размер массива.

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

    Наконец, я упомяну ваше использование

    real(c_double), bind(c) :: degF, degC
    

    в модуле.

    Это интероперабельные глобальные переменные (через ассоциативную связь). Вы не ссылаетесь на эти переменные в коде Fortran: пустышка и результат функции - это не эти вещи.


    В этом простом случае из вышеперечисленного и другого ответа у вас будет такая подпрограмма, как

    subroutine DegCtoF(n, degC, degF) bind(c,name='DegCtoF')
      ...
    end subroutine
    

    но это, возможно, хорошая возможность описать использование дескриптора C из ISO_Fortran_binding.h. Однако обратите внимание, что в ближайшей перспективе gfortran не поддерживает этот подход.

    Рассмотрим источник на Фортране

    subroutine DegCtoF(degC, degF) bind(c,name='DegCtoF')
       use, intrinsic :: iso_c_binding, only : c_double
       implicit none
       real(c_double), intent(in), dimension(:) :: degC
       real(c_double), intent(out), dimension(*) :: degF
    
       degF(1:SIZE(degC)) = degC*1.8+32
    end subroutine DegCtoF
    

    (для простоты я предполагаю, что управление памятью degF осуществляется полностью на стороне C - естественно, можно было бы выйти за пределы массива предполагаемого размера). Чтобы эта подпрограмма могла взаимодействовать, аргумент, соответствующий degC, должен быть указателем на CFI_cdesc_t.

    Возьмите код C (с магическими числами размера)

    #include "ISO_Fortran_binding.h"
    #include <stdio.h>
    
    void DegCtoF(CFI_cdesc_t*, double*);
    
    int main(int argc, char *argv[])
    {
        printf("C and Fortran together!\n");
    
        CFI_CDESC_T(1) DegreesC_Fdesc;
        CFI_index_t extent[1] = {2};
        CFI_rank_t rank = 1;
    
        double DegreesC[2] = {32, 64};
        double DegreesF[2];
    
        CFI_establish((CFI_cdesc_t*)&DegreesC_Fdesc, &DegreesC, CFI_attribute_other, 
                      CFI_type_double, 2*sizeof(double), rank, extent);
    
        DegCtoF((CFI_cdesc_t*)&DegreesC_Fdesc, DegreesF);
        printf("%3.1f [C] = %3.1f [F]\n", DegreesC[0], DegreesF[0] );
        printf("%3.1f [C] = %3.1f [F]\n", DegreesC[1], DegreesF[1] );
    
        return 0;
    }
    

    Здесь CFI_establish устанавливает подходящий дескриптор C DegreesC_Fdesc, который может соответствовать фиктивному аргументу Фортрана предполагаемой формы. Внутри подпрограммы Fortran нет никаких проблем с оценкой размера входящего массива.

    02.01.2016
  • Спасибо за понимание. 02.01.2016
  • Новые материалы

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

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

    Обзор: Машинное обучение: классификация
    Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

    Разработка расширений Qlik Sense с qExt
    Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..