목차
수치계산을 위한 프로그램을 짜다 보면, 포트란 함수를 C 혹은 C++ 에서 쓰거나, 그 반대의 경우가 생길 때가 있습니다. 특히 역사가 좀 오래된 모델을 가져다 쓸 때 그렇죠. C언어에서 함수를 특정한 포맷으로 써주면, 오브젝트 파일들을 링크함으로써, 언어의 장벽을 뛰어넘을 수 있습니다.
소스 코드로부터 프로그램을 만드는 과정에서 등장하는 오브젝트 파일의 개념이 낮설게 느껴지거나, 여러개의 소스 파일로부터 프로그램을 만드는 구체적인 과정이 궁금하신 분들은 다음 포스팅을 읽어보시길 권장합니다.
공통사항
C언어에서 프로토타입을 선언할때, 포트란 subroutine 이름 뒤에 _를 붙이고 모든 파라미터는 포인터가 됩니다. 포트란 subroutine이 call-by-reference 방식으로 작동하기 때문이죠. 그리고 따로 주어지는 리턴값이 없으니, 리턴 변수형은 void가 될 것입니다.
예를 들어서 SUBROUTINE DOWHATEVER(INTEGER INPUT) 이라는 포트란 함수를 C언어에서 사용하려면, 다음과 같은 프로토타입의 선언이 필요합니다.
void dowhatever_(int *input);
반대로 C언어로 쓰여진 함수를 포트란에서 사용하고 싶다면,
void dowhatever_(int *input) {
...
함수의 내용
...
}
의 형식으로 정의해 주어야 합니다.
C 메인 프로그램에서 Fortran subroutine 사용하기
SUBROUTINE DOWHATEVER(INTEGER INPUT) 이라는 함수가 정의된 포트란 소스파일 dowhatever.f 를 가지고 있는데 이걸 main.c 의 메인함수에서 쓰고 싶다면, 이렇게 합시다.
#include<stdio.h>
void dowhatever_(int *input);
int main(int argc, char *argv[]) {
int input;
...
dowhatever_(&input);
...
}
라고 main.c 소스파일을 작성한 뒤에, 컴파일을 다음과 같이 합니다.
gfortran -c dowhatever.f -o dowhatever.o
gcc -c main.c -o main.o
gcc -o main dowhatever.o main.o
참고로 C++ 의 경우는
#include<stdio.h>
extern "C" {
void dowhatever_(int *input);
}
using namespace std;
int main(int argc, char *argv[]) {
int input;
...
dowhatever_(&input);
...
}
라고 main.cpp 소스파일을 작성한 뒤에 C++ 컴파일러를 이용해서 컴파일을 합니다.
gfortran -c dowhatever.f -o dowhatever.o
g++ -c main.cpp -o main.o
g++ -o main dowhatever.o main.o -lg2c
만약 GNU 컴파일러 (gfortran 및 g++)를 이용해서 컴파일을 하는데 링크 오류가 난다면, -lg2c 대신 -lgfortran (libgfortan) 라이브러리를 사용해서 링크하면 문제를 해결할 수 있습니다.
이제 푸리에 변환을 이용한 예시를 하나 소개해 볼까 합니다. 이 프로그램에 사용되는 푸리에 변환 라이브러리인 FFTW는 별도의 포스팅에 소개되어 있고요.
여기서도 마찬가지로 가우스 함수의 푸리에 변환을 다뤄볼텐데, 차이점이 있다면 변환대상이 되는 가우스함수의 형태를 포트란 소스파일에서 정의하고, 이를 C++ 메인 프로그램에서 사용한다는 점입니다. 이를 위해서 FFTW 라이브러리와 함께 다음 소스 및 헤더 파일들을 사용했습니다.
func_gaussian.f [다운로드]
FFTransformer.h [다운로드]
FFTransformer.cpp [다운로드]
test1_cxx_fort_fftw.cpp [다운로드]
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include"FFTransformer.h"
extern "C" {
void func_gaussian_(double *func,
double *x, double *mean_x, double *sigma_x);
}
typedef struct InfoGauss {
// mean and width of a Gaussian function
double mean;
double sigma;
} InfoGauss;
double func_fft_re(double q, void *ptr_param);
double func_fft_im(double q, void *ptr_param);
int main(int argc, char *argv[]) {
// Determine the actual values for mean and width.
double mean_q = 1.;
double sigma_q = 0.5;
// range in the q-space
double q_min = mean_q - 10. * sigma_q;
double q_max = mean_q + 10. * sigma_q;
// lattice spacing in the q-space
double delta_q = 0.02;
FFTransformer fft_transform;
// Initialize the Fourier transformation class
bool fft_init = fft_transform.init(q_min, q_max, delta_q, false);
if (!fft_init) {
fprintf(stderr,
"ERROR : FFTransformer is not properly initialized.\n");
exit(1);
}
InfoGauss info_gauss;
info_gauss.mean = mean_q;
info_gauss.sigma = sigma_q;
// function pointer
PtrFuncFFT ptr_func_in;
ptr_func_in.re = &func_fft_re; // real part
ptr_func_in.im = &func_fft_im; // imaginary part
ptr_func_in.param = &info_gauss; // parameter
// Perform fast Fourier transformation
bool fft_next = fft_transform.next(ptr_func_in);
if (!fft_next) {
fprintf(stderr,
"ERROR : FFTransformer failed to perform.\n");
exit(1);
}
char filename_output[200];
if (argc > 1) {
strcpy(filename_output, argv[1]);
} else {
strcpy(filename_output, "func2_gaussian_fft.dat");
}
fft_transform.write(filename_output);
return 0;
}
// We are doing Fourier transform of a real Gaussian function.
double func_fft_re(double q, void *ptr_param) {
InfoGauss *ptr_info = (InfoGauss *)ptr_param;
double mean_q = ptr_info->mean;
double sigma_q = ptr_info->sigma;
double func_now;
func_gaussian_(&func_now, &q, &mean_q, &sigma_q);
return func_now;
}
// So the imaginary part would be zero.
double func_fft_im(double q, void *ptr_param) {
return 0.;
}
main 함수가 있는 소스파일에는 가우스 함수를 정의한 func_gaussian_ 함수의 프로토타입이 있습니다. 이 함수는 포트란 소스파일 func_gaussian.f 에서 서브루틴으로 정의되어 있고, 가우스 함수의 평균과 표준편차 등을 매개변수로 받고 있죠. 추가로 푸리에 변환의 대상이 되는 복소수 함수의 실수 및 허수부분을 func_fft_re 및 func_fft_im 이라는 함수들에서 정의하고 있는데요. 여기서는 가우스 실수함수의 변환을 다루고 있는 관계로 func_fft_re 함수에서 func_gaussian_ 함수를 호출하는 반면에, func_fft_im 함수는 0을 리턴합니다.
추가적으로 FFTransformer 라는 클래스가 도입되어 있는데, 이는 변환대상이 되는 함수의 포인터를 받아서 푸리에 변환을 수행하는 객체입니다. 자세한 사항은 다음 포스팅에 소개되어 있습니다.
다음과 같이 소스파일들을 컴파일해서 오브젝트 파일들을 생성한 다음, 이를 FFTW 라이브러리와 함께 링크하면 실행파일을 얻을 수 있습니다.
gfortran func_gaussian.f -c
g++ FFTransformer.cpp -c
g++ test1_cxx_fort_fftw.cpp -c
g++ test1_cxx_fort_fftw.o FFTransformer.o func_gaussian.o -lfftw3 -lm -lgfortran -o [실행파일 이름]
프로그램을 실행해 보면, 위에 링크된 포스팅에 나온 것과 동일한 결과를 얻을 수 있습니다.
Fortran 프로그램에서 C언어 함수 사용하기
이번에는 반대로 void dowhatever_(int *input) { ... } 이 정의된 소스파일 dowhatever.c 를 포트란 메인함수가 있는 main.f 에서 가져다 써 봅시다. 포트란에서는 그냥 CALL만 해주면 됩니다.
PROGRAM MAIN
...
INTEGER INPUT
...
CALL DOWHATEVER(INPUT)
...
RETURN
END
라고 main.f 소스파일을 작성하고, 앞의 경우와 마찬가지로 오브젝트 파일들을 링크시켜서 실행파일을 얻습니다.
gcc -c dowhatever.c -o dowhatever.o
gfortran -c main.f -o main.o
gfortran -o main dowhatever.o main.o
출처 : www.yolinux.com/TUTORIALS
이 웹사이트에는 함수 이외에 common block에 대한 설명도 있습니다.
같이 알고 있으면 좋은 C/C++ 팁들
명령행 인자
라이브러리 제작
동적 메모리 할당 (malloc)