본문 바로가기

Studying/Computer Programs

C/C++와 Fortran을 조합한 프로그래밍

목차

  1. 공통사항

  2. C 메인 프로그램에서 Fortran subroutine 사용하기

  3. Fortran 프로그램에서 C언어 함수 사용하기

 

수치계산을 위한 프로그램을 짜다 보면, 포트란 함수를 C 혹은 C++ 에서 쓰거나, 그 반대의 경우가 생길 때가 있습니다. 특히 역사가 좀 오래된 모델을 가져다 쓸 때 그렇죠. C언어에서 함수를 특정한 포맷으로 써주면, 오브젝트 파일들을 링크함으로써, 언어의 장벽을 뛰어넘을 수 있습니다.

 

 

소스 코드로부터 프로그램을 만드는 과정에서 등장하는 오브젝트 파일의 개념이 낮설게 느껴지거나, 여러개의 소스 파일로부터 프로그램을 만드는 구체적인 과정이 궁금하신 분들은 다음 포스팅을 읽어보시길 권장합니다.

 

 

C/C++ 코드가 프로그램이 되는 과정

여기서는 C언어 또는 C++로 작성된 소스 파일, 헤더 파일의 개념과 이들을 빌드하여 하나의 프로그램을 만드는 과정에 대해 간략하게 짚어보겠습니다. 여러 개의 소스, 헤더 파일들로 이루어진

swstar.tistory.com

 

공통사항

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는 별도의 포스팅에 소개되어 있고요.

 

 

FFTW : C/C++ 고속 푸리에 변환 라이브러리

라이브러리 소개 현재 진행중인 연구에서 사용중인 C언어 푸리에 변환 라이브러리인 FFTW에 대해 간략히 포스팅해볼까 합니다. FFTW는 Fastest Fourier Transform in the West 의 약자인데요. 저 같은 토종 아

swstar.tistory.com

 

여기서도 마찬가지로 가우스 함수의 푸리에 변환을 다뤄볼텐데, 차이점이 있다면 변환대상이 되는 가우스함수의 형태를 포트란 소스파일에서 정의하고, 이를 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_refunc_fft_im 이라는 함수들에서 정의하고 있는데요. 여기서는 가우스 실수함수의 변환을 다루고 있는 관계로 func_fft_re 함수에서 func_gaussian_ 함수를 호출하는 반면에, func_fft_im 함수는 0을 리턴합니다.

추가적으로 FFTransformer 라는 클래스가 도입되어 있는데, 이는 변환대상이 되는 함수의 포인터를 받아서 푸리에 변환을 수행하는 객체입니다. 자세한 사항은 다음 포스팅에 소개되어 있습니다.

 

 

C/C++ 에서 함수를 매개변수로 사용하기

함수 포인터를 이용한 구현 일반적으로 C언어나 C++ 에서 사용하는 함수의 경우, 인자(매개변수) 혹은 파라미터로 변수를 받아갑니다. 이 값들을 가지고 정의된 기능을 수행하게 되죠. 하지만 프

swstar.tistory.com

 

다음과 같이 소스파일들을 컴파일해서 오브젝트 파일들을 생성한 다음, 이를 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

 

Tutorial: Using C/C++ and Fortran together

Comparison of FORTRAN and C/C++ datatypes: FORTRAN C/C++ byte unsigned char integer*2 short int integer long int or int integer iabc(2,3) int iabc[3][2]; logical long int or int logical*1 bool (C++, One byte) real float real*8 double real*16 long double co

www.yolinux.com

이 웹사이트에는 함수 이외에 common block에 대한 설명도 있습니다.

 


 

같이 알고 있으면 좋은 C/C++ 팁들

 

명령행 인자

 

Command-Line Arguments (C/C++ 공통)

C++를 배우기 위해 책을 보는데, command-line arguments에 대한 내용이 있었다. 메인함수를 특별한 방법으로 정의해서, 프로그램을 실행시킬때 커맨드 라인에서 옵션을 지정해줄 수 있다고 한다. int mai

swstar.tistory.com

 

라이브러리 제작

 

C언어 라이브러리 만들기

자주 쓰는 함수들을 라이브러리 형태로 만들어 두면, 프로그램을 짤 때 편리합니다. 예를 들어서 함수 Function이 mylib.c 라는 소스코드에 정의되어 있고, 프로토타입이 mylib.h 라는 헤더 파일에 선

swstar.tistory.com

 

동적 메모리 할당 (malloc)

 

C 언어의 malloc 함수

학부때부터 수치계산 할때면 항상 C언어를 써 왔지만, 배열과 포인터의 등가관계에 대해서는 극히 최근에야 알게 되었다. 예를 들어서 1) double a[10]; 을 선언했다면, 배열의 이름 a는 포인터 변수

swstar.tistory.com