2016-01-02 13 views
5

Tôi đang cố chuyển một mảng từ C/C++ vào mô-đun Fortran 2003 và nhận các giá trị được tính toán trở lại C/C++. Tôi đã có thể vượt qua và trả lại các giá trị đơn (vô hướng) chỉ tốt, nhưng nhận được một mảng trở lại và chứng minh là khó khăn. Tôi đã tìm thấy nhiều chủ đề về các giá trị vô hướng và tôi đã thành công trong việc đưa ra những công việc đó.Vượt qua các mảng từ C/C++ đến Fortran và trả về một mảng được tính toán

Tôi đã lập mô hình các hàm dựa trên mảng của mình sau các hàm vô hướng làm việc của tôi.

Tôi đang sử dụng gcc/gfortran.

Đây là mô-đun 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 

Và 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; 
} 

Và cuối cùng nhưng không kém, 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 
+0

Vâng, tôi không chắc chắn về Fortran, nhưng trong C và C++, đi qua một mảng (thực sự chuyển địa chỉ của phần tử đầu tiên) mất tất cả thông tin kích thước, vì vậy thường lệ Fortran của bạn không biết gì về số lượng phần tử thực tế mảng chứa. Tôi sẽ mong đợi hai tham số, mảng (hoặc con trỏ đến bộ đệm), và số lượng các phần tử. – PaulMcKenzie

+0

Từ thẻ của bạn, tôi cho rằng bạn đang sử dụng gfortran/gcc, nhưng bạn có thể xác nhận không? Thông qua một đối số hình dạng giả định, chẳng hạn như 'degC' không phải là một phần của khả năng tương tác Fortran 2003 C và đòi hỏi nhiều nỗ lực hơn nữa. Khía cạnh hiện được mong đợi là một phần của F2015 (ISO TS) không được hỗ trợ bởi gcc. Trong trường hợp không hỗ trợ như vậy, hãy sử dụng chú thích bằng @PaulMcKenzie và sử dụng một mảng hình dạng rõ ràng với kích thước cũng được chuyển. – francescalus

+0

@francescalus, Vâng tôi đang sử dụng gfortran/gcc. Khi bạn có nghĩa là nỗ lực nhiều hơn, Tôi có cần phải vượt qua kích thước của mảng đó không? Tôi biết rằng khi C gọi hàm Fortran, nó sẽ chuyển các giá trị vô hướng bằng một con trỏ. Tôi chỉ cần phải vượt qua kích thước cũng có, và sau đó nó có thể (Fortran) con số phần còn lại ra? –

Trả lời

5

Trước francescalus khẳng định nó, tôi sẽ nói rằng từ những gì tôi biết rằng đã được một chút cũ, khả năng tương tác không cho phép những gì bạn đang cố gắng làm với mảng. Ngoài ra, một số thói quen tốt luôn quan trọng khi viết mã. Ví dụ: sử dụng implicit none trong fortran để buộc khai báo tất cả các biến trước khi chúng được sử dụng. Việc sử dụng hằng số được đặt tên khi ngôn ngữ cho phép nó, ví dụ: 2 mà bạn đang sử dụng làm kích thước mảng trong fortran.

Dưới đây là phiên bản đã sửa đổi của mã của bạn nên thực hiện điều gì đó giống như những gì bạn muốn đạt được.

// 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; 
} 
+0

Đó là những gì tôi đang tìm kiếm. Chỉ cần kiểm tra mã và nó hoạt động tuyệt vời. Những gì tôi không chắc chắn là sự an toàn của việc sử dụng một chương trình con mặc dù. Đến từ IDL, các chương trình con cho phép khối mã sửa đổi bất kỳ biến nội bộ nào trong khối. Về cơ bản một chương trình con được thực hiện nội dòng. Có giống nhau ở Fortran không? Điều tôi tò mò là nếu tôi đã nói một biến có tên là NUM trong chương trình con, thì chương trình con có thể sửa đổi biến đó theo nghĩa toàn cục không? –

+0

Biến nó thành một chương trình con là một điều tôi quên đề cập đến. Đó là cách tôi sử dụng để làm điều đó vài năm trước đây. Nhờ @francescalus đề cập đến nó trong câu trả lời của mình. Tôi không biết gì về IDL. FOC việc thực hiện nội tuyến, tôi không hiểu câu hỏi của bạn. – innoSPG

5

Theo quy định của hiện tại Fortran (Fortran năm 2008, nhưng điều này là như nhau cho khi C khả năng tương tác được giới thiệu vào Fortran 2003), thủ tục Fortran không tương thích với C nếu nó có một đối số giả định hình dạng giả định (các hạn chế khác cũng được áp dụng). Trong mã của bạn degC, lập luận giả trong hàm DegCtoF, khai báo là

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

là một điều như vậy.

Vì vậy, dưới F2003 bạn không thể có một chức năng tương thích. Đó là nơi mọi thứ trở nên phức tạp.

Trong bản dự thảo được đề xuất cho F2015 (dựa trên ISO TS29113 Further Interoperability of Fortran with C) một điều như vậy có thể tương tác. Và cú pháp này là (tôi nghĩ) được hỗ trợ bởi các phiên bản gcc gần đây, đó là lý do tại sao mã không bị từ chối bởi gfortran.

(TS) Chuẩn hóa tương tác với quy trình như vậy với đối số hình dạng giả định, yêu cầu sử dụng bộ mô tả C được mô tả trong ISO_Fortran_binding.h ở mặt C là không được triển khai trong gcc. Để thực hiện tương tác như vậy, thay vào đó, yêu cầu hiểu trực tiếp bộ mô tả mảng gcc.

Nhưng bạn thật may mắn.Trong trường hợp của bạn, bạn không thực sự cần phải sử dụng một giả định giả định hình dạng giả: bạn có thể sử dụng một đối số giả định hình dạng rõ ràng và tương tác như vậy là một phần của F2003. Tất cả những gì bạn cần làm là vượt qua kích thước của mảng.

Dù bằng cách nào, hàm tương thích phải trả lại kết quả vô hướng, vì vậy bạn cũng sẽ muốn chuyển sang chương trình con, như được nêu trong answer by innoSPG.

Cuối cùng, tôi sẽ đề cập đến việc bạn sử dụng

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

trong mô-đun.

Đây là các biến toàn cục có thể tương tác (thông qua liên kết liên kết). Bạn không tham chiếu các biến này trong mã Fortran: giả và kết quả hàm không phải là những thứ này.


Trong trường hợp này đơn giản từ bên trên, và câu trả lời khác, người ta sẽ hạnh phúc có một chương trình con như

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

nhưng điều này có lẽ là một cơ hội tốt để mô tả việc sử dụng các mô tả C từ ISO_Fortran_binding.h. Tuy nhiên, lưu ý rằng trong thời hạn trước, gfortran không hỗ trợ phương pháp này.

Hãy xem xét các nguồn Fortran

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 

(vì đơn giản tôi sẽ cho rằng công tác quản lý bộ nhớ của degF được thực hiện tất cả ở phía bên C - một cách tự nhiên người ta có thể mở rộng ra ngoài các mảng kích thước giả định). Đối với chương trình con này có thể tương thích với đối số tương ứng với degC phải là một con trỏ đến CFI_cdesc_t.

Hãy mã C (với kích thước con số ma thuật)

#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; 
} 

Đây CFI_establish thiết lập một C mô tả phù hợp DegreesC_Fdesc mà có thể tương ứng với các hình dạng giả định Fortran luận dummy. Bên trong chương trình con Fortran, không có vấn đề gì khi đánh giá kích thước của mảng đến.

+0

Cảm ơn bạn đã thấu hiểu. –

Các vấn đề liên quan