python에서 c/cpp 코드 사용하기
목차
개요
방법
동적 라이브러리 빌드
C
libadd.c
코드
1
2
3
4
5
6
#include "libadd.h"
int add(int a, int b)
{
return a + b;
}
libadd.h
코드
1
2
3
4
5
6
#ifndef LIBADD_H
#define LIBADD_H
int add(int a, int b);
#endif /* LIBADD_H */
빌드 진행
오브젝트 파일을 만들고
1
gcc -c -fPIC -I. -o libadd.o *.c
동적 라이브러리로 만든다.
리눅스는 so, 윈도우는 dll, 맥은 dylib 확장자를 사용한다.
1
gcc -shared libadd.o -o libadd.so
gcc 옵션 링크 : http://jangpd007.tistory.com/220
동적 라이브러리에 대한 설명 : https://nomad-programmer.tistory.com/105
test.py
라는 이름으로 파이썬 파일 생성 / 코드 작성 후
python3 test.py
명령어를 통해 실행
1
2
3
4
5
6
7
from os.path import dirname, abspath
import ctypes
CUR_PATH = dirname(abspath(__file__))
libc = ctypes.CDLL(CUR_PATH + "/libadd.so")
print(libc.add(1, 2))
C++
libprint.h
코드
libprint.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
#ifndef LIBPRINT_H
#define LIBPRINT_H
#include <iostream>
#include <cstdlib>
#include <cstdint>
#include <cstring>
class Foo
{
char *var;
public:
Foo(char *var, int len);
~Foo();
void bar();
};
extern "C" {
Foo* Foo_new(char *str, int len);
void Foo_bar(Foo* foo);
void Foo_del(Foo* foo);
void print_str(char *str);
}
#endif /* LIBPRINT_H */
libprint.cpp
코드
libprint.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
#include "libprint.h"
Foo::Foo(char *var, int len) {
this->var = (char *)std::calloc(len + 1, sizeof(char));
std::memcpy(this->var, var, len);
}
Foo::~Foo() {
if (this->var != NULL) {
free(this->var);
}
}
void Foo::bar() {
std::cout << "Hello + var : " << this->var << std::endl;
}
extern "C" Foo *Foo_new(char *var, int len)
{
// return nullptr;
return new Foo(var, len);
}
extern "C" void Foo_bar(Foo* foo) {
foo->bar();
}
extern "C" void Foo_del(Foo* foo) {
foo->~Foo();
}
extern "C" void print_str(char *str)
{
if (NULL == str) {
std::cout << "string is NULL" << std::endl;
return ;
}
std::cout << "string is " << str << std::endl;
printf("string is %s\n", str);
}
빌드 진행
1
2
g++ -c -fPIC -I. -o libprint.o *.cpp
g++ -shared libprint.o -o libprint.so
test.py
파일 생성 및 코드 작성 및 실행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from os.path import dirname, abspath
import ctypes
CUR_PATH = dirname(abspath(__file__))
libcpp = ctypes.CDLL(CUR_PATH + "/libprint.so")
string = "Hello World"
libcpp.print_str(ctypes.c_char_p(bytes(string, "utf-8")))
var = libcpp.Foo_new(ctypes.c_char_p(string.encode()), len(string))
# nullptr 리턴 시 var의 값은 0이 된다.
if var is 0:
print("Error alloc C++ class")
exit(1)
libcpp.Foo_bar(var)
libcpp.Foo_del(var)
del var
자료형
C/C++의 함수 Parameter로 넘기기 위해서는 해당 자료형을 명시해줘야 한다.
https://docs.python.org/3/library/ctypes.html#fundamental-data-types 해당 페이지에서 자료형을 확인할 수 있다.
char *
자료형을 넘기기 위해서는 ctypes.c_char_p
를 사용한다.
Array 등의 파이썬 리스트를 넘기는 것이 상당히 불편하다는 것을 알 수 있다.
참조 : https://devocean.sk.com/blog/techBoardDetail.do?ID=163835
또한 잘못된 Argument를 넘기거나 C/C++ 특성상 공유 라이브러리 내 로직 등의 문제가 생길 경우 아래 메시지와 같은 Segmentation Fault 등으로 파이썬 스크립트가 사망할 수 있으니 사용에 있어 각별한 주의가 필요하다.
[1] 5100 segmentation fault ./libadd.py