天天看點

Fortran與C語言,回調函數的使用比較

參考:《Advanced Fortran 90: Callbacks with the Transfer Function》 Author:Drew McCormack   

http://www.macresearch.org/advanced_fortran_90_callbacks_with_the_transfer_function

回調函數:

C語言中的回調函數用起來很簡單,隻要定義一個與要調用的函數具有相同參數和傳回參數的函數指針就可以使用了,例如:

#include <stdio.h>

void Woof()  
{
    printf("Woof\n");
}

void Meouw() 
{
    printf("Meouw\n");
}

typedef void (*SoundFunction)();
void MakeSoundTenTimes(SoundFunction soundFunc)
{
    int i;  
    for ( i = 0; i < 10; ++i ) soundFunc();
}

int main()  
{
    MakeSoundTenTimes(Woof);
    MakeSoundTenTimes(Meouw);
}
           

在主程式中通過把Woof和Meouw傳給

MakeSoundTenTimes

,在

MakeSoundTenTimes

裡就會分别調用Woof和Meouw函數來執行。

在Fortran90中要實作上面類似的功能要怎麼做呢? 舉例如下:

module Sounds

contains

  subroutine Woof()
    print *,'Woof'
  end subroutine

  subroutine Meouw() 
    print *,'Meouw'
  end subroutine

  subroutine MakeSoundTenTimes(soundFunc)
    integer :: i
    interface
      subroutine soundFunc()
      end subroutine
    end interface
    do i = 1, 10 
      call soundFunc()
    enddo
  end subroutine

end module

program main
  use Sounds
  call MakeSoundTenTimes(Woof)
  call MakeSoundTenTimes(Meouw)
end program
           

看起來也很簡單!

上面的回調函數都沒有傳遞參數,如果回調函數需要傳遞參數,而且被調用函數的參數不一樣,那又該怎麼辦?

其實在C語言中很簡單,隻要在将被調用的函數的參數的類型設為void*,舉例如下:

#include <stdio.h>

void IncrementAndPrintFloat(void *data)  
{
    double *d = data; 
    (*d)++; 
    printf("%f\n", *d);
}

void IncrementAndPrintInteger(void *data)  
{
    int *i = data; 
    (*i)++; 
    printf("%d\n", *i);
}

typedef void (*IncrementFunction)(void*);
void IncrementTenTimes(IncrementFunction incrFunc, void *data)
{
    int i;  
    for ( i = 0; i < 10; ++i ) incrFunc(data);
}

int main()  
{
    double f = 5.0;
    int i = 10; 
    IncrementTenTimes(IncrementAndPrintFloat, &f);
    IncrementTenTimes(IncrementAndPrintInteger, &i);
}
           

但是在Fortran90中并沒有類似的Void*的功能,那該怎麼辦呢?  不急,雖然Fortran90中沒有Void*但是有transfer函數啊!

下面是采用transfer函數來實作類似以上功能的例程:

module Increments

contains

  subroutine IncrementAndPrintReal(data)
    character(len=1) :: data(:)
    real             :: r
    r = transfer(data, r)
    r = r + 1.0 
    print *,r
    data = transfer(r, data)
  end subroutine

  subroutine IncrementAndPrintInteger(data)
    character(len=1) :: data(:) 
    integer          :: i    
    i = transfer(data, i)
    i = i + 1 
    print *,i
    data = transfer(i, data)
  end subroutine

  subroutine IncrementTenTimes(incrFunc, data)
    character(len=1) :: data(:) 
    integer :: i
    interface
      subroutine incrFunc(data)
        character(len=1) :: data(:) 
      end subroutine
    end interface
    do i = 1, 10 
      call incrFunc(data)
    enddo   
  end subroutine

end module

program main
  use Increments
  character(len=1), allocatable :: data(:) 
  integer                       :: lengthData
  real                          :: r = 5.0 
  integer                       :: i = 10

  lengthData = size(transfer(r, data))
  allocate(data(lengthData))
  data = transfer(r, data)
  call IncrementTenTimes(IncrementAndPrintReal, data)
  deallocate(data)

  lengthData = size(transfer(i, data))
  allocate(data(lengthData))
  data = transfer(i, data)
  call IncrementTenTimes(IncrementAndPrintInteger, data)

end program
           

實作起來也不太複雜。

transfer在資料類型的轉換中是非常有用的,如果想了解更詳細的内容請參考文章開頭介紹的文章。

繼續閱讀