본문 바로가기

Programming/C/C++

exturn "C"


[출처] : http://jimbo73.egloos.com/tb/1486292
extern "C"에 대하여 알아보자.
(사실 이글은, 해당 카테고리에 글이 하나도 없음이 너무 애처로워(?), 여러 글들을 참고하여 날림으로 작성한것이다. ^^)

컴파일러는 링커가 링킹작업시 오브젝트간 함수 이용 및 위치를 파악할 수 있도록, 컴파일시 사용된 함수에 관련한 정보를 오브젝트 파일에 기록하며, 이러한 정보를 linkage라고 한다.

그런데 C 와 C++을 혼합하여 사용 시, 함수이용에 문제가 발생할수 있는데, 이는 두 언어간의 linkage방식이 틀리는데 기인한다.

linkage이란 컴파일 시 함수이름 앞 또는 뒤에 '_' 등의 심볼을 덧붙이는 것을 말하는것으로, C 와 C++은 컴파일시 오브젝트 파일에 함수명, 변수명등에 심볼을 기록하는 방식이 다르다.

C에서는 함수의 이름이 유일하기 때문에(즉 overloading을 지원하지 않기때문에) 함수 앞에 '_' 등의 심볼만을 붙이면 된다. 하지만 C++에서는 overloading을 지원하기 때문에 같은 이름의 함수를 여러개 가질 수 있으므로, 이에 따라 함수를 구분하기 위해서는, 함수이름만으로 구분할 수 없게된다. 그러므로 인자의 개수와 데이터형에 대한 정보까지 넣게되어(이러한 방식을 mangled name이라한다.), linkage 정보가 상이하게 된다.

그러므로 C와 C++ 을 혼합하는 프로그램에서는, link시 함수명을 각각의 방식으로 찾을 수 있도록 C인지, C++인지를 명시해주어야 하며, 이때 extern "C" 는 linkage에 대한 지시자 역할을 한다.

extern "C"는 C++의 linkage방식을 사용하지 않고, C의 linkage방식을 사용하라고 컴파일러에게 알려주는 역할을 한다.

#ifdef __cplusplus
extern "C" {
#endif

int func1();
int func2();

#ifdef __cplusplus
}
#endif


[출처] : http://alaveiw.tistory.com/32

앞선 post에서 쓰여있지만 -- 내가 쓴 것은 아니지만 -- 잠시 더 C++에서 C를 사용하는 것에 대해 
잠시 더 언급해 보자.참고로 이 post는 내가 쓴 것임... ㅋㅋ

아래와 같은 헤더파일과 소스파일을 각각 a.h와 a.c로 작성, 저장하였다고 하자.

// a.h -----------------------------------

#ifndef _A_H
#define _A_H

#include " stdio.h " 

int add(int a, int b);
int add(int a, int b, int c);

#endif

//---------------------------------------


// a.c -----------------------------------

#include " a.h "

int add(int a, int b)
{
  return a + b;
}

int add(int a, int b, int c)
{
  return a + b + c;
}

//---------------------------------------

이 소스를 c++컴파일러인 g++을 가지고 컴파일 해보면 에러 없이 정상적으로 컴파일되어
a.o라는 오브젝트 파일을 생성해 낸다.

> g++ -I. -c a.c

이 오브젝트 파일내에 무슨내용이 있는 지 알아 보기 위해 nm이라는 명령어를 사용하여
한번 살펴보자.

> nm a.o

결과값:
00000000 T _Z3addii
0000000c T _Z3addiii

위 결과값을 보면 add라는 2개의 함수에 대한 정보임을 대충 감으로 느낄 수 있을 것이다.
근데 앞에 _Z3, 뒤에 ii 또는 iii가 붙어 있음을 알 수 있다.
앞에 _Z는 모르겠지만 뒤에 붙은 ii나 iii는 add함수에 대한 parameter가 각각 interger 2개, integer 3개임을
나타낸다고 유추해 볼수 있다. 이렇듯 오브젝트 파일의 함수이름에 추가적인 정보가 붙는 것을 
function name mangling이라고 한다. 

이제 이 헤더파일과 소스파일을 c컴파일러인 gcc로 한번 컴파일 해 보자.

> gcc -I. -c a.c

이 명령을 실행해 보면 아래와 같은 에러가 뜸을 알 수 있다. 

./a.h:7: error: conflicting types for 'add'
./a.h:6: error: previous declaration of 'add' was here
./a.h:7: error: conflicting types for 'add'
./a.h:6: error: previous declaration of 'add' was here
a.c:4: error: conflicting types for 'add'
./a.h:7: error: previous declaration of 'add' was here
a.c:4: error: conflicting types for 'add'
./a.h:7: error: previous declaration of 'add' was here
a.c:9: error: conflicting types for 'add'
a.c:4: error: previous definition of 'add' was here

내용인 즉은, 바로 add라는 함수가 두번 정의되어 있다는 것이다.
다시 말하면, C에서는 paramter의 수나 타입에 상관없이 동일한 이름의 함수를
하나이상 사용할 수 없음을 의미한다.
위에서는 add라는 함수가 두개 있다는 것을 의미한다. 이는
C++의 주요한 기능중의 하나인 function overloading이 C에는 존재하지 않음을 알려 준다.

우리의 목적이 gcc로 만든 오브젝트파일의 내용을 보기위함이므로 add함수를 하나 제거하고 다시 컴파일을 해보자.

// a.h -----------------------------------

#ifndef _A_H
#define _A_H


#include " stdio.h "

int add(int a, int b);

#endif

// a.c -----------------------------------

#include " a.h "

int add(int a, int b)
{
  return a + b;
}

//---------------------------------------

> gcc -I. -c a.c

컴파일은 정상적으로 되며 오브젝트 파일의 내용을 살펴보면 아래와 같은 내용이 출력됨을 
알 수 있다.

> nm a.o

결과값:
00000000 T add

gcc 컴파일러로 컴파일하면 function name mangling이 일어나지 않음을 알 수 있다.

문제는 여기서 발생한다. C로 작성된 object파일을 C++에서 사용하려면
C++ 컴파일러가 만드는 function name mangling때문에
사용할 수 없게 된다.
해결책은 C++에서 C함수를 사용할 때 function name mangling을 하지 말라고 하는 수 밖에 없다. 이 때 쓰는 명령이 extern "C"이다. 

// a.h ------------------------

#ifndef _A_H
#define _A_H

#include " stdio.h "

extern "C" int add(int a, int b);

#endif
// ---------------------------

이렇게 추가 한후 g++로 컴파일 하고 nm을 해 보면 아래와 같이 function name mangling이 일어나지
않음을 알 수 있다.

> g++ -I. -c a.c

> nm a.o

결과값:
00000000 T add


위 소스를 C컴파일러 gcc로 하면 당연히 아래와 같이 에러가 난다. 왜냐하면 extern "C"이 C에서 쓰이는 keyword가 
아니기 때문이다. 

> gcc -I. -c a.c

In file included from a.c:1:
./a.h:6: error: syntax error before string constant


그러면 위의 소스를 C와 C++에서 같이 사용하려면 어떻게 해야 할까?

방법은 그 유명한 #ifdef ~ #endif를 사용하는 것이다.
모든 C++컴파일러는 __cplusplus라는 것을 내부적으로 정의해 놓고 있다. 위에서 사용해온 g++컴파일러도 __cplusplus를 정의해 놓고 있다는 얘기다.
뒤집어 말하면 현재 컴파일러에 __cplusplus가 정의되어 있다면 현 컴파일러는 C++컴파일러라는 말이다.
반대로, gcc에는 __cplusplus가 정의되어 있지 않다.

결국, #ifdef __cplusplus #endif를 이용하여 소스를 약간 변형해 보면

// a.h ------------------------
#ifndef _A_H
#define _A_H

#include " stdio.h "

#ifdef __cplusplus
extern "C"
{
#endif

int add(int a, int b);  

#ifdef __cplusplus
}
#endif

#endif

// ----------------------------


처럼 변경할 수 있다. 

이제 gcc나 g++로 컴파일해도 정상적으로 컴파일 됨을 확인할 수 있다.
> gcc -I. -c a.c
> g++ -I. -c a.c


[추가사항] ; 컴파일 관련
g++컴파일러를 사용해서 gcc로 컴파일된 라이브러리를 사용할 일이 생겼다.

gcc로 컴파일 된 라이브러리를 g++로 다시 컴파일 하기 어려웠기 때문이다.

그래서 여기저기 검색한 결과 아주 쉬운 방법이 있었다.

 

//현재 여기는 main.cpp파일

#include<iostream>

extern "C"

{

#include"Ccompiled.h"//C로 컴파일된 라이브러리의 헤더

}

int main()

{

    //cpp라이브러리를 사용하여 프로그래밍 한다~~

    return 0;

}

 

=========컴파일 과정=========

$g++ -o prog main.cpp -L. -lclibfile

 

gcc로 컴파일 된 라이브러리 이름은 libclibfile.a 라고 가정한다.

빨간 글씨로 된 extern "C" { } 의 방법을 사용하면 g++로 컴파일할 때 gcc로 컴파일 된 라이브러리를 사용가능하다.

 

이거때문에 아주 편하게 작업할 수 있다. 나이쓰 ㅋㅋ

이 방법 알아내는데 검색어 넣기가 쉽지 않았다. 네이버에도 한방에 안나온다.

역시 구글이 이런거 찾는데는 최고다. 네이버 블로그에 올려놓으면 많은 사람들이 쉽게 찾아 볼 수 있을 것이다.

 

 

 

 

How do I call a C function from C++?

Just declare the C function ``extern "C"'' (in your C++ code) and call it (from your C or C++ code). For example:

	// C++ code

extern "C" void f(int); // one way

extern "C" { // another way
int g(double);
double h();
};

void code(int i, double d)
{
f(i);
int ii = g(d);
double dd = h();
// ...
}

The definitions of the functions may look like this:

	/* C code: */

void f(int i)
{
/* ... */
}

int g(double d)
{
/* ... */
}

double h()
{
/* ... */
}

Note that C++ type rules, not C rules, are used. So you can't call function declared ``extern "C"'' with the wrong number of argument. For example:

	// C++ code

void more_code(int i, double d)
{
double dd = h(i,d); // error: unexpected arguments
// ...
}

How do I call a C++ function from C?

Just declare the C++ function ``extern "C"'' (in your C++ code) and call it (from your C or C++ code). For example:

	// C++ code:

extern "C" void f(int);

void f(int i)
{
// ...
}

Now f() can be used like this:

	/* C code: */

void f(int);

void cc(int i)
{
f(i);
/* ... */
}

Naturally, this works only for non-member functions. If you want to call member functions (incl. virtual functions) from C, you need to provide a simple wrapper. For example:

	// C++ code:

class C {
// ...
virtual double f(int);
};

extern "C" double call_C_f(C* p, int i) // wrapper function
{
return p->f(i);
}

Now C::f() can be used like this:

	/* C code: */

double call_C_f(struct C* p, int i);

void ccc(struct C* p, int i)
{
double d = call_C_f(p,i);
/* ... */
}

If you want to call overloaded functions from C, you must provide wrappers with distinct names for the C code to use. For example:

	// C++ code:

void f(int);
void f(double);

extern "C" void f_i(int i) { f(i); }
extern "C" void f_d(double d) { f(d); }

Now the f() functions can be used like this:

	/* C code: */

void f_i(int);
void f_d(double);

void cccc(int i,double d)
{
f_i(i);
f_d(d);
/* ... */
}

Note that these techniques can be used to call a C++ library from C code even if you cannot (or do not want to) modify the C++ headers.

[출처]: http://www.research.att.com/~bs/bs_faq2.html#callCpp



'Programming > C/C++' 카테고리의 다른 글

stl <sort> 함수 사용하기  (0) 2009.04.13
아스키 코드표  (0) 2009.04.02
C언어 가변 인자  (0) 2008.12.24
vsprintf  (0) 2008.12.24
함수포인터  (0) 2008.12.01