Как вызвать функцию C в Fortran и правильно передать аргументы uint32_t

Привет, я использую код Fortran 90 для вызова функции C. Поскольку я манипулирую адресами, аргументы функции C должны быть правильно сопоставлены в Fortran. Я использую ifort и icc для компиляции кода и работаю на 64-битной машине.

Некоторое тестирование показало, что это будет работать и с int32_t, хотя во избежание возможных ловушек я хотел бы сохранить uint32_t

Функции C, которые я вызываю, имеют следующие прототипы

uint32_t encode_(uint32_t x, uint32_t y)
uint32_t decode_(uint32_t dec)

Я не могу вызывать эти функции, просто делая что-то вроде

integer :: cod,encode
cod = encode(i,j)

Это произведет тарабарщину. Поэтому я использую обходной путь:

void code2d_(uint32_t j[] ){


uint32_t i;

i=encode_(j[0],j[1]);  
// the underscore is due to the FORTRAN naming convention

printf("Coded %10d  \n",i);

}

И впоследствии в Фортране

 integer :: cod,code2d
 cod = code2d(i,j)

Ну, очевидно, у меня есть проблема с несоответствием типов аргументов. К сожалению, я не знаю, как это исправить. Поскольку в моих функциях декодирования/кодирования выполняется арифметика двоичных адресов, очень важно сохранить uint32_t.


person Alexander Cska    schedule 13.07.2014    source источник


Ответы (2)


Похоже, вы знаете об iso_c_binding, так как используете этот тег. Изучите взаимодействие Fortran 2003 с C. Прочтите описание тега и некоторую документацию, например http://gcc.gnu.org/onlinedocs/gcc-4.9.0/gfortran/Interoperability-with-C.html . В современном Фортране нет места для ваших конечных подчеркиваний и подобных вещей.

В Fortran нет неподписанных типов, вы должны использовать подписанные. Пока значения со знаком положительные, это работает. Если вам нужны большие значения, используйте больший целочисленный тип. Вы можете transfer() младшие байты преобразовать в int32, если вам это нужно.

В-третьих, Фортран по умолчанию использует некоторые варианты передачи по ссылке, особенно для bind(c) процедур (это может быть ссылка на копию или какой-то другой вариант). Вы должны использовать атрибут value для передачи по значению.

uint32_t encode(uint32_t x, uint32_t y)
uint32_t decode(uint32_t dec)

module c_procs
  interface
    function encode(x, y) bind(C, name="encode")
      use iso_c_binding
      integer(c_int32_t) :: encode
      integer(c_int32_t), value :: x, y
    end function
    function decode(x, y) bind(C, name="decode")
      use iso_c_binding
      integer(c_int32_t) :: decode
      integer(c_int32_t), value :: dec
    end function
  end interface
end module

...

use iso_c_binding
use c_procs

integer(c_int32_t) :: cod, i, j
cod = encode(i,j)

Последние версии GCC способны обнаруживать, что мы смешиваем подписанные и неподписанные типы во время оптимизации времени компоновки:

rng.f90:173:0: warning: type of 'sub' does not match original declaration [-Wlto-type-mismatch]
     ival = sub(jz, jsr)
^
rng_c.c:2:10: note: return value type mismatch
 uint32_t sub(uint32_t a, uint32_t b) {
          ^
/usr/include/stdint.h:51:23: note: type 'uint32_t' should match type 'int'
 typedef unsigned int  uint32_t;
                       ^
rng_c.c:2:10: note: 'sub' was previously declared here
 uint32_t sub(uint32_t a, uint32_t b) {

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

person Vladimir F    schedule 13.07.2014
comment
Спасибо за комментарий и очень удобное решение. Я надеялся выполнить работу без привязки ISO C. Это функция FORTRAN 95 и более поздних версий, которая может вызвать некоторые проблемы. Например, я часто использую F77 для быстрых операций с массивами. Там я использую C для распределения памяти, и обычная привязка работает довольно аккуратно (элемент подчеркивания), как вы могли бы это назвать. В любом случае я буду полагаться на современный FORTRAN и использовать привязку ISO C. Кстати, во втором интерфейсе у вас должно быть integer(c_int32_t) :: decode Спасибо, Владимир! - person Alexander Cska; 14.07.2014
comment
Вы всегда можете сохранить символы подчеркивания, просто используйте их в имени привязки. Вы также можете просто исправить изменение процедур C, чтобы принимать указатели. В любом случае обязательно точно придерживайтесь интерфейса. Привязка Fortran-C очень эффективна, не беспокойтесь о производительности. - person Vladimir F; 14.07.2014

Вы можете написать функцию C, которая принимает C_INT (или int в C), а затем преобразует его в uint32_t. Затем вы ссылаетесь на это из Фортрана.

in C:

uint32_t to_uint32_t ( int i ) {
    return (uint32_t)i; //cast actually not needed since it implicitly converts
}

в фортране:

module convert
    interface
        integer(C_INT32_T) function integer_to_uint32_t_C ( i ) bind (c,name="to_uint32_t")
            use iso_c_binding
            use user32_constants_types
            integer(c_int), value :: i
        end function
    end interface

end module convert

Затем вы можете использовать integer_to_uint32_t_C для передачи целочисленных значений Fortran функциям C, которые ожидают uint32_t. Кроме того, вы можете захотеть создать функцию C, которая преобразует обратно в обычный ole int, чтобы вы могли использовать результаты в Fortran.

person annoying_squid    schedule 01.05.2019
comment
В чем причина STDCALL? Обратите внимание, что ОП сказал, что они работают на 64-й машине. Но что более важно, как можно было бы вызывать encode_ и decode_ с ними? - person Vladimir F; 01.05.2019
comment
@VladimirF Да, я полагаю, в данном случае это не нужно, поэтому избавился от него. Обычно он используется при связывании с динамическими библиотеками, использующими STDCALL. - person annoying_squid; 01.05.2019