이글은 예전 제 싸이월드 블로그에 작성한 글을 티스토리로 옮긴 것입니다.

원문 : http://cy.cyworld.com/home/21147242/post/4E4A12146415739A07C68401
원문 작성일 : 2011.08.16

------------------------------------------------------------------------

 

아래 테스트는 리눅스 환경에서 테스트하였습니다.
C++ 에서 작성된 myList.h, myList.cpp 라는 파일이 있다고 가정합니다.
c 로 작성된 main.c 파일이 있습니다.

main.c 가 myList.h 파일을 include 하여 해당 기능을 수행할 수 있도록 함이 목적입니다.
어떤 C++ 코드를 하나 만들어 봅니다.
C 에서의 사용을 고려하지 않은 C++ 코드는 아래와 같습니다.

 

=================================== myList.h

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef _MYLIST_H_
#define _MYLIST_H_
 
#include<list>
using namespace std ;
 
typedef list<int>MyList ;
typedef list<int>::iterator MyListIter ;
 
class CMyList
{
public :
        int addList(int val) ;
        int getSize() ;
 
 
private :
        MyList m_list ;
        MyListIter m_listIter ;
};
 
#endif // _MYLIST_H_
 

 

======================================= myList.cpp

 
1
2
3
4
5
6
7
8
9
10
11
12
#include "myList.h"
 
int CMyList::addList(int val)
{
        m_list.push_back(val) ;
        return 1 ;
}
 
int CMyList::getSize()
{
        return m_list.size() ;
}

 

위 코드를 gcc로 컴파일하면 당연히 에러가 나겠죠.

아래와 같이 C 에서 컴파일할 수 있도록 작업을 해 줍니다.

 

1. C++ 전용(?) 코드는 #ifdef __cplusplus 로 막아줍니다.
   위 코드에선 STL 선언부랑 클래스 선언부가 해당될 겁니다.

2. C 에서 호출할 수 있도록 함수를 추가하여줍니다.
   int MyList_addList(int val) ;
   int MyList_getSize() ;

3. 구현부에서 name magling 이 안일어나도록 extern "C" 를 선언해 줍니다.
   위 조건대로 수정하면 아래와 같이 수정될 것입니다.

=============================================== myList.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef _MYLIST_H_
#define _MYLIST_H_
 
#ifdef __cplusplus
 
#include<list>
 
using namespace std ;
 
typedef list<int>               MyList ;
typedef list<int>::iterator     MyListIter ;
 
class CMyList
{
public :
        int addList(int val) ;
        int getSize() ;
 
private :
        MyList m_list ;
        MyListIter m_listIter ;
};
 
#else
 
void* MyList_getInstance() ;
 
int MyList_addList(void* instance, int val) ;
int MyList_getSize(void* instance ) ;
 
void TEST_destory(void* instance) ;
 
#endif
#endif // _MYLIST_H_

 

================================================= myList.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "myList.h"
 
int CMyList::addList(int val)
{
        m_list.push_back(val) ;
        return 1 ;
}
 
int CMyList::getSize()
{
        return m_list.size() ;
}
 
extern "C"
{
 
void* MyList_getInstance()
{
        CMyList* myList= new CMyList ;
        return (void*)myList ;
}
 
int MyList_addList(void* instance, int val)
{
        CMyList * myList = (CMyList*)instance ;
        myList->addList(val) ;
 
        return 1 ;
}
 
int MyList_getSize(void* instance )
{
        CMyList* myList = (CMyList*)instance ;
        return myList->getSize() ;
}
 
int MyList_destroy(void* instance)
{
        CMyList* myList = (CMyList*)instance ;
        delete myList ;
}
 
}

 

이제 C 전용(?)으로 선언된 아래 함수를 가지고 main.c 에서 써먹어 봅니다.

void* MyList_getInstance() ;
int MyList_addList(void* instance, int val) ;
int MyList_getSize(void* instance ) ;
void TEST_destory(void* instance) ;

=============================================== main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
#include "myList.h"
 
int main()
{
        void* myList = MyList_getInstance() ;
        int size = 0;
 
        MyList_addList(myList, 3) ;
        MyList_addList(myList, 8) ;
 
        size = MyList_getSize(myList) ;
 
        printf("size : %d\n", size) ;
 
        MyList_destroy(myList) ;
        return 1 ;
}

 

C++ 의 클래스 객체를 void* 로 대체하였습니다. 핸들처럼요.
C 나 C++ 컴파일러는 object 파일을 생성하는데 이 object 파일은 C, C++ 모두 공통으로 사용할 수 있습니다.
그래서 위와같이 살짝 수정하여 서로 호출이 가능할 수 있습니다.

정리하면,

1. 헤더파일의 C++ 코드는 C 컴파일러가 안보이도록 막고,
막은 부분을 대처할 수 있는 함수를 만들어줍니다.
#ifdef __cplusplus 부분이 이에 해당합니다.

2. C++ 컴파일러는 함수 오버로딩 때문에 함수 이름을 변경하여 object 파일을 생성합니다.
(Name mangling 관련 부분은 여기서 자세히 언급하지 않겠습니다.)
하지만 C 컴파일러는 함수 오버로딩을 지원하지 않습니다.

예들 들어,

test()

란 함수가 있다고 하면,
이를 C++ 컴파일러로 컴파일하면


testzx6e()

->이런식으로 변경되어 object 파일에 저장됩니다.

C 컴파일러로 컴파일하면

test()

그대로 저장됩니다.

즉, C 컴파일러는 사용자가

test()

라고 호출하면 object 파일에서 직관적으로

test()

를 찾습니다.

그런데 만약 object 파일을 C++ 컴파일러로 컴파일했다면...

test()

함수가

testzx6e()

로 저장되어있기에 C 컴파일러는


testzx6e()

->이 함수를 못찾겠죠.

이 때 사용하는 방법이, C++ 컴파일러에게, 이 파일들은 C 에서 사용될 예정이니
name mangling 을 수행하지 마라, 즉 object 파일 생성 시,


test()

test()

로 저장하라고 알려주는거죠.

이게 myList.cpp 의

extern "C"

부분입니다.

C++ 코드는 object 로 먼저 생성을 해야 C 에서 사용할 수 있습니다.
이제 마지막으로 Makefile 을 작성합니다.
최종 실행파일 생성 시, -lstdc++ 하는 부분에 주의합니다.
================================================= Makefile


main : myList.o main.c

        gcc -o $@ $^ -lstdc++


myList.o : myList.cpp

        g++ -c -o $@ $^


clean :
        rm -rf *.so *.o


=====================================================

-lstdc++ 부분은 아래 홈페이지에서 정보를 얻었습니다.
아래 분께서는 저와는 다른 방법 (구조체를 클래스처럼 쓰셨네요.) 으로
C에서 C++ 을 호출하고 계시니 참고하여 읽어보시기 바랍니다.


http://ospace.tistory.com/215

 

+ Recent posts