2011. 7. 4. 17:55

♧ C로 작성된 함수를 C++에서 호출하고자 할 경우,

혹은 반대로 C++로 작성된 함수를 C프로그램에서 호출하고자 할 경우.

   

[ C/C++와 심볼명 ]

▷ 아래의 dbg 함수를 C/C++컴파일러로 각각 컴파일해 보자.

/************

* dbg.c

************/

#include <stdio.h>

void dbg(const char *s) {

    printf("Log: %s\n", s);

}

# C 컴파일 :: gcc -W -Wall -c dbg.c

# C++ 컴파일 :: g++ -W -Wall -c dbg.c

   

▷ C컴파일 / 심볼명 확인

▷ C++컴파일 / 심볼명 확인

   

▷ 컴파일해서 생성된 오브젝트 파일에 포함된 심볼명.

컴파일러

심볼명

C

dbg

C++

_Z3dbgPKc

   

☞ C 컴파일러로 함수를 컴파일 하면 기본적으로는 함수명이 그대로 심볼명이 된다.

시스템 환경에 따라서는 심볼명이 "dbg"가 아니라 "_dbg"가 될 경우도 있지만 크게 다르지는 않다.

한편, C++ 컴파일러로 함수를 컴파일한 경우 함수가 속한 이름공간(namespace) 정보

또는 함수 인자의 자료형 정보가 심볼명에 포함되어 나타난다.

  

   

   

[ C++에서 C함수 호출하기 ]

▶ dbg.c를 C컴파일러로 컴파일한 후 C++로 작성된 함수(sample.cpp)에서 호출해 보자.

① sample.cpp 작성

/****************

* sample.cpp

****************/

extern "C" void dbg(const char *s);

int main() {

dbg("foo");

return 0;

}

② sample.cpp 컴파일, 링크하면 정상적으로 실행 파일이 생성/실행

이상 없이 C++함수(sample.cpp)에서 C함수(dbg)를 호출 하였다.

☞ sample.cpp의 『extern "C"』가 포인트다.

   

▶ 『extern "C"』의 유무에 따라 sample.o의 변화.

① 『extern "C"』가 있을 경우.

② 『extern "C"』가 없을 경우.

☞ ②에서 "sample.o"는 "_z3dbgPKc"라는 심볼을 참조하지만,

dbg.o에 포함된 심볼은 단지 "dbg""_z3dbgPKc"는 심볼은 어디에도 없다.

   

▷ 따라서 extern "C"를 넣지 않으면 다음과 같이 링크할 때 실패하게 된다.

이와 같이 C++에서 C함수를 호출할 때 extern "C"가 중요한 역할을 한다.

   

〔주의〕 extern "C"를 붙이면 인자의 자료형 일치 여부를 검사하지 않는다.

① sample.cpp의 dbg자료형 변경.

/****************

* sample.cpp

****************/

//extern "C" void dbg(const char *s);

extern "C" void dbg(int i); // 잘못된 함수 원형 선언

int main() {

dbg("4444");

return 0;

}

② 컴파일, 링크, 실행.

☞ 정상적으로 링크는 되지만 생성된 실행 파일은 정상적으로 실행 되지 않는다.

   

〔왜! 링크는 성공한 것일까?〕

sample.o가 참조하고 있는 것은 어디까지나 자료형 정보를 포함하지 않은 심볼 "dbg"로,

dbg.o에 포함되어 있는 심볼명과 일치하기 때문이다.

"링크에 성공하면 잘못된 자료형으로 함수가 호출되지 않는다."라고 생각하기 쉽지만,

C++라도 extern "C"를 사용한 부분인 경우에 한해서는 그렇지 않다.

이는 컴파일러에서 자료형을 검사하는 안전성 수준이 C언어 수준으로 저하되기 때문이다.

   

▷ 이러한 문제를 미연에 방지하려면 C언어 측의 헤더 파일을

C++에서도 이용할 수 있도록 준비해 두어야 한다.

예를 들면, 아래의 dbg.h와 같은 헤더 파일을 작성하는 것이 바람직하다.

/**************

* dbg.h

**************/

#ifdef __cplusplus

extern "C" {

#endif

void dbg(const char *s);

#ifdef __cplusplus

}

#endif

  

   

   

[ C에서 C++함수 호출하기 ]

▷ 이번에는 반대로 C에서 C++ 함수를 호출해 보도록 하자.

『최대공약수를 구하는 함수』를 Boost C++ Library를 이용해

C++로 간단히 구현하고, 그 함수를 C에서 호출해 보자.

   

〔중요한 점 두가지〕
º C++함수를 C 링키지(extern "C")로 컴파일.

º 링크는 gcc가 아닌, g++로 수행.

   

▷ C++측 구현

/***********

* gcd.h

***********/

#ifdef __cplusplus

extern "C"

#endif

int gcd (int v1, int v2);

/****************

* gcd.cpp

****************/

#include <boost/math/common_factor.hpp>

extern "C" {

int gcd (int v1, int v2) {

return boost::math::gcd(v1, v2);

}

}

   

▷ C++함수를 호출하는 C프로그램은 특별히 달라진 것이 없다.

/*************

* sample.c

*************/

#include <stdio.h>

#include "gcd.h"

   

int main() {

printf("%d와 %d의 최대공약수는 %d(이)다.\n", 14, 35, gcd(14,35));

return 0;

}

   

▶ 컴파일, 실행.

  

   

 

〔 주의 사항 〕

C++함수는 C함수로 예외 처리를 넘겨서는 안됨.

C언어에서 호출되는 C++함수를 작성할 때는 C++의 예외가 C함수로 도달하지 않도록 주의.

C++의 예외가 C언어로 작성된 함수에 도달된 경우의 함수 동작에 대해 C++표준 규격에는

명확히 정의되어 있지 않다. 예를 들면 GCC에서는 프로세스가 이상 종료되는 현상이 나타남.

C++ 함수에서 명시적인 예외를 throw하지 않도록 신경 쓰는 것은 물론,

▷ 다음과 같은 점도 충분히 주의해야 한다.

º new 연산자가 std::bad_alloc 예외를 throw할 가능성

º std::vector의 멤버 함수 at에서 std::out_of_range 예외를 throw할 가능성

   

함수 포인터를 다루는 C함수에 주의.

C함수 내부에서 C++의 예외가 발생하는 것을 막을 수 없는 경우가 있다.

다음과 같이 표준 C의 qsort함수를 C++에서 사용한 경우를 생각해 보자.

/**************

* sample2.cpp

**************/

#include <cstdlib>

using namespace std;

   

// qsort 비교 함수

int compar(const void *, const void *) {

throw -1;

}

   

int main() {

int array[] = {3, 2, 1};

try {

qsort(array, 3, sizeof(int), compar);

} catch(...) {

return -1;

}

return 0;

}

☞ qsort 함수에 인자로 넘긴 비교함수 compar가 예외를 throw하기 때문에

qsort함수내부에서 C++의 예외가 발생하게 된다. GCC를 사용할 경우에

이와 같은 경우라도 프로세스가 이상 종료되지 않기 위해서는 아래와 같이

qsort 함수를 "-fexceptions"옵션을 지정해서 컴파일 해야 한다.

% gcc -fexceptions qsrot.c

   

일반적으로 함수 포인터를 다루는 C함수는 -fexceptions옵션을 지정해서 컴파일해야

오류가 발생하지 않는다. glibc의 Makefile을 보면 이러한 함수(qsort, bsearch 등)는 이 옵션을

지정해서 컴파일 하도록 되어 있다.

 

Posted by devanix