簡體   English   中英

Fortran iso_c_binding 標准:錯誤:(1) 處的參數“test_variable”中的類型不匹配; 將 INTEGER(8) 傳遞給 TYPE(c_ptr)

[英]Fortran iso_c_binding standard: Error: Type mismatch in argument 'test_variable' at (1); passed INTEGER(8) to TYPE(c_ptr)

我的現實生活中的程序使用整數 * 8 變量並且不使用這個特定的 iso_c_binding 補丁編譯,見下文。 當不使用 iso_c_binding package 時,我的程序運行良好。

在我的程序中使用 iso_c_binding package 時,我收到以下錯誤消息:

錯誤:(1) 處的參數“test_variable”中的類型不匹配; 將 INTEGER(8) 傳遞給 TYPE(c_ptr)

我從我的程序中創建了以下測試用例。 我的程序無法發布。 測試用例演示了我遇到的錯誤。

問題1:你如何解決這個問題? 如果可能的話,以下是我想要的。 test.c 文件中的兩個結構實際上存在於我的程序中。 游戲 function 正式版 arguments 也確實存在於我的真實世界程序中。 我的程序將整數*8 變量傳遞給游戲例程。 除了這三件事之外的其他一切都應該能夠被修改。

其他問題: C 代碼是否必須更改? 不算iso_c_binding接口代碼,Fortran代碼是不是要改? 有什么需要改變的?

iso_c_binding 可以做到這一點嗎?

謝謝,

文件 the_game.f


  module the_game

  use, intrinsic :: iso_c_binding, only: c_int, c_float, c_ptr

  type, bind(c) :: myfloat
    real(c_float) :: float_tmp
  end type

  type, bind(c) :: myint
    integer(c_int) :: int_tmp
  end type

  interface
    function game(myint1, myfloat2) bind(c, name="game_")
        import :: c_int, c_ptr
        implicit none
        type(c_ptr) :: myint1
        type(c_ptr) :: myfloat2
        integer(c_int) :: game
    end function
  end interface

  end module the_game

文件 main.f:


   program main
     use the_game
     integer*8 test_variable
     integer*8 test_variable2
     integer ans

     ans=game (test_variable,test_variable2)
   end program main

文件test.c:

#include <stdio.h>

struct myfloat 
{
   float float_tmp;
};

struct myint 
{
   int int_tmp;
};


int game_(myint1,myfloat2)
struct myint **myint1;
struct myfloat **myfloat2;
{
 /* printf ("(*myint1)->int_tmp = %d\n",(*myint1)->int_tmp); */
 /* printf ("(*myfloat2)->float_tmp = %f\n",(*myfloat2)->float_tmp); */
 /* (*myint1)->int_tmp = 1; */
 /* (*myfloat2)->float_tmp = 100.1; */
  return(0);
}

匯編:

  gcc      -g -c test.c     -o test.o 
  gfortran -g -c the_game.f -o the_game.o
  gfortran -g -c main.f     -o main.o
  gfortran -g    main.o test.o   -o main.exe   -lgfortran

Output:

main.f:9.19:

         ans=game (test_variable,test_variable2)                        
                   1
Error: Type mismatch in argument 'myint1' at (1); passed INTEGER(8) to TYPE(c_ptr)

備選方案 1:以下 main.f 不起作用:

參考: https://wiki.scorec.rpi.edu/wiki/Iso_c_binding

       program main
         use the_game
         integer*8, allocatable, target :: test_variable1
         integer*8, allocatable, target :: test_variable2
         integer ans

         type(c_ptr) :: cptr1
         type(c_ptr) :: cptr2

         allocate (test_variable1)
         allocate (test_variable2)
  
         cptr1 = c_loc(test_variable1)
         cptr2 = c_loc(test_variable2)

         ans=game (cptr1,cptr2)
       end program main
       

Output:

 
main.f:14.17:

         cptr1 = c_loc(test_variable1)                                  
                 1
Error: Can't convert REAL(4) to TYPE(c_ptr) at (1)
main.f:15.17:

         cptr2 = c_loc(test_variable2)                                  
                 1
Error: Can't convert REAL(4) to TYPE(c_ptr) at (1)
       

備選方案 2:取消注釋 test.c 時,以下 main.f 不起作用:

文件 main.f:


       program main
         use the_game
         TYPE(c_ptr) test_variable
         TYPE(c_ptr) test_variable2

         integer ans

         ans=game (test_variable,test_variable2)
       end program main
    #include <stdio.h>

    struct myfloat 
    {
       float float_tmp;
    };

    struct myint 
    {
       int int_tmp;
    };

   int game_(myint1,myfloat2)
   struct myint **myint1;
   struct myfloat **myfloat2;
    {
  
      /*printf ("(*myint1)->int_tmp = %d\n",(*myint1)->int_tmp);
      printf ("(*myfloat2)->float_tmp = %f\n",(*myfloat2)->float_tmp);
      (*myint1)->int_tmp = 1;
      (*myfloat2)->float_tmp = 2.0;*/

      return(0);
    }

如果取消注釋 test.c 中的 4 個語句,則運行可執行文件:test.c 中的最后一條語句導致段錯誤:

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

line 5:  3541 Segmentation fault      ./main.exe

備選方案 3:對代碼的以下 main.f 更改不起作用:

文件 main.f:


       program main
         use the_game
         integer*8 test_variable
         integer*8 test_variable2
         integer ans

         type(c_ptr) :: cptr1
         type(c_ptr) :: cptr2
    
         cptr1=c_loc(test_variable1)
         cptr2=c_loc(test_variable2)

         test_variable1 = 90
         test_variable2 = 1000
         ans=game (cptr1,cptr2)
       end program main

匯編:


main.f:11.25:

           cptr1=c_loc(test_variable1)                                  
                         1
Error: Can't convert REAL(4) to TYPE(c_ptr) at (1)
main.f:12.26:

          cptr2=c_loc(test_variable2)                                 
                          1
Error: Can't convert REAL(4) to TYPE(c_ptr) at (1)

備選方案 4:對代碼的以下 main.f 更改無法編譯:


       program main
         use iso_c_binding
         use the_game
         TYPE(c_ptr) test_variable
         TYPE(c_ptr) test_variable2

         integer*8 value1
         integer*8 value2
    
         integer ans
         test_variable=c_loc(value1)
         test_variable2=c_loc(value2)

         ans=game (test_variable,test_variable2)
       end program main

編譯錯誤:

main.f:12.31:

          test_variable=c_loc(value1)                                  
                               1
Error: Parameter 'value1' to 'c_loc' at (1) must be either a TARGET or an associated pointer
main.f:13.32:

          test_variable2=c_loc(value2)                                 
                                1
Error: Parameter 'value2' to 'c_loc' at (1) must be either a TARGET or an associated pointer

錯誤:(1) 處的參數“test_variable”中的類型不匹配; 將 INTEGER(8) 傳遞給 TYPE(c_ptr)

是的, INTEGER(8)是與TYPE(c_ptr)不同的類型。 不允許將前一種類型的參數與后一種類型的子程序參數相關聯。

問題1:你如何解決這個問題? 如果可能的話,以下是我想要的。 test.c 文件中的兩個結構實際上存在於我的程序中。

test.c 中沒有任何結構對象。 有兩種結構類型聲明,但沒有證據表明這些類型的對象。 這是必然的,因為不清楚您期望在哪里實際定義任何此類對象,或者由誰分配。

游戲 function 正式版 arguments 也確實存在於我的真實世界程序中。

當然正式的 arguments 到game_() function 是存在的。 他們在 function 的定義中的聲明是必需的。 但也許你在談論他們指向的對象?

我的程序將整數*8 變量傳遞給游戲例程。

至少你的編譯器可能不會抱怨。 參數是指針,而不是整數。 I would expect a C compiler to emit an analogous complaint when processing an attempt to call the game_() function in C with arguments that were integers of any size. C 指針不是整數,在 C 和 Fortran 中都不是。

除了這三件事之外的其他一切都應該能夠被修改。

這里有很多噪音和不清晰,但我認為你真正想要的是

  • Fortran 代碼定義兩種結構類型的實例,以及
  • 將指向這些對象的指針作為 arguments 傳遞給 C game_() function,
  • 以便后者可以訪問其成員。

您已經對integer*8integer(8) (不一定是同一件事)給予了很多關注,但這些並不是工作的正確工具。 我懷疑您在 C 方面還使用了比需要或有用的更多級別的間接。

由於您使用的是 GNU 編譯器套件,因此您應該考慮閱讀其關於 Fortran / C 互操作性特性的文檔,這還不錯。

您至少有以下注意事項需要處理:

  • 聲明與您的 C 結構類型可互操作的 Fortran 派生類型
  • 使用最適合您實際需要的參數類型定義您的 C function
  • 為您的 C function 定義適當的可互操作接口
  • 從 Fortran 正確調用 C function。

我認為你實際上使它變得比雙方都需要的更難。

您似乎確實整理出了可互操作的類型聲明。

C function 聲明似乎是另一回事。 我看不出有什么合理的理由讓你想要接收指向結構的雙指針,結果可能不是你想的那樣。 無論您希望 Fortran 代碼將單個結構與每個參數或它們的數組相關聯,您可能希望 C 端將參數聲明為單個指針。 另請注意,盡管您可以使用帶有下划線的 C function 名稱,但您不需要這樣做。 只需指定您在 Fortran 接口中選擇的任何名稱即可。

例子:

測試.c

#include <stdio.h>

struct myfloat {
   float float_tmp;
};

struct myint {
   int int_tmp;
};

int game_(struct myint *myint1, struct myfloat *myfloat2) {
    puts("C says:");
    printf ("myint1->int_tmp = %d\n", myint1->int_tmp);
    printf ("myfloat2->float_tmp = %f\n", myfloat2->float_tmp);

    myint1->int_tmp = 1;
    myfloat2->float_tmp = 2.0;

    return 0;
}

請特別注意,arguments 只使用了一級間接。

Fortran 接口也非常簡單:

the_game.f90

module the_game

  use, intrinsic :: iso_c_binding, only: c_int, c_float, c_ptr

  type, bind(c) :: myfloat
    real(c_float) :: float_tmp
  end type

  type, bind(c) :: myint
    integer(c_int) :: int_tmp
  end type

  interface
    function game(myint1, myfloat2) bind(c, name="game_")
        import :: c_int, myfloat, myint
        implicit none
        type(myint) :: myint1      ! not a pointer!
        type(myfloat) :: myfloat2  ! not a pointer!
        integer(c_int) :: game
    end function
  end interface

end module the_game

請注意,function 參數未定義為指針。 Fortran 默認會傳遞指針,因為它的默認調用 suibprogram 調用語義使用(大約)通過引用傳遞。 主要是當您想要按值傳遞時(正如您經常做的那樣,因為這些是 C 的語義),您需要在聲明的接口中做任何特殊的事情。

有了它,從 Fortran 中調用它並不難。 例子:

main.f90

program main

use the_game

type(myint) :: test_variable
type(myfloat) :: test_variable2
integer ans

test_variable%int_tmp = 7
test_variable2%float_tmp = 3.14

ans = game(test_variable,test_variable2)

write(*, *)
write(*, *) 'Fortran says:'
write(*, *) 'Result = ', ans, '; my int = ', test_variable%int_tmp,     &
                              '; my_float = ', test_variable2%float_tmp

end program main

請注意,在調用 C integer的 Fortran 代碼中既沒有任何c_ptr也沒有任何對應的 integer Fortran 端僅根據(可互操作的)Fortran 派生類型工作。 這是 output,表明雙方都在訪問相同的對象:

 $./main.exe C says: myint1->int_tmp = 7 myfloat2->float_tmp = 3.140000 Fortran says: Result = 0; my int = 1; my_float = 2.00000000

或者

后來的評論表明,也許您打算使用game_()的參數將 C 指針傳回 Fortran 調用者,也許是指向動態分配空間的指針。 這也可以適應,在這種情況下,您必須處理c_ptr ,因為這就是 C 將發出的。 But you do have to take care with level of indirection -- Fortran will pass an object of type c_ptr to C by reference , which C will receive as a double pointer.

一旦返回 C 指針值,Fortran 將使用c_f_pointer()子例程將適當類型的 Fortran 指針與分配的空間相關聯。 例子:

alttest.c

#include <stdio.h>
#include <stdlib.h>

struct myfloat {
   float float_tmp;
};

struct myint {
   int int_tmp;
};

int game_(struct myint **myint1, struct myfloat **myfloat2) {
    *myint1 = malloc(sizeof(**myint1));
    *myfloat2 = malloc(sizeof(**myfloat2));

    if (!*myint1 || !*myfloat2) {
        return -1;
    }

    (*myint1)->int_tmp = 1;
    (*myfloat2)->float_tmp = 2.0;

    return 0;
}

the_game_alt.f90

module the_game_alt

  use, intrinsic :: iso_c_binding, only: c_int, c_float, c_ptr

  type, bind(c) :: myfloat
    real(c_float) :: float_tmp
  end type

  type, bind(c) :: myint
    integer(c_int) :: int_tmp
  end type

  interface
    function game(myint1_p, myfloat2_p) bind(c, name="game_")
        import :: c_int, c_ptr
        implicit none
        type(c_ptr), intent(out) :: myint1_p
  nd in this case you do have to deal with `c_ptr`      type(c_ptr), intent(out) :: myfloat2_p
        integer(c_int) :: game
    end function
  end interface

end module the_game_alt

altmain.f90

program main

use the_game_alt
use, intrinsic :: iso_c_binding

type(myint), pointer :: intvar
type(myfloat), pointer :: floatvar
type(c_ptr) :: myint_ptr, myfloat_ptr
integer ans


ans = game(myint_ptr, myfloat_ptr)
if (ans .ne. 0) then
    write(*, *) 'error: C returned ', ans
else
    call c_f_pointer(myint_ptr, intvar)
    call c_f_pointer(myfloat_ptr, floatvar)

    write(*, *)
    write(*, *) 'Fortran says:'
    write(*, *) 'Result = ', ans, '; my int = ', intvar%int_tmp, &
                                  '; my_float = ', floatvar%float_tmp
end if
end program main

程序 output:

 $./altmain.exe Fortran says: Result = 0; my int = 1; my_float = 2.00000000

注意

目前還不清楚你真正想要做什么。 實際上,可能需要與上述任何一種方法不同的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM