Golang
CGO를 사용하여 Golang과 CPP간 배열을 주고받는 방법
장대한 삽질 이야기
2024. 4. 10. 15:37
Go의 가장 큰 특징중 하나가 Cgo Package를 통해 C와 연동이 가능하다는 것이다.
Cgo lets Go packages call C code. Given a Go source file written with some special features, cgo outputs Go and C files that can be combined into a single Go package.
하지만 연동 객체로 Param을 주고받기가 은근 난해해서 이참에 정리해본다.
CPP에서는 Go의 자원을 사용할 수 없기에 기본적으로 CPP중심으로 연동된다.
배열을 Go에서CPP로 넘기는 경우
배열을 Cpp로 넘기는 경우 배열의 포인터와 길이를 넘겨야한다.
- Go
func GotoCpp(items []string) {
var cPtritems **C.char
var cItemLen C.int
if cItemLen = C.int(len(cPtritems)); cItemLen > 0 {
cItems := make([]*C.char, len(items))
for i, item := range items {
cItems[i] = C.CString(item)
// 빼먹지 않도록 주의한다.
defer C.free(unsafe.Pointer(cItems[i]))
}
cPtritems = (**C.char)(unsafe.Pointer(&cItems[0]))
}
// call C function
C.GotoCpp(cPtritems, cItemLen)
}
- Cpp
int GotoCpp(const Item* arrItems, const int nItemLen) {
for ( int i = 0; i < nItemLen; i++ )
{
const int integerValue = arrItems[ i ].nValue;
const int stringValue = arrItems[ i ].lpszValue;
// Do Something...
}
}
Struct type의 배열을 Go에서 CPP로 넘기는 경우
구조체 배열을 넘겨주는 경우 CPP에서 선언하고, 이를 사용해야 한다.
넘기는 방법은 상단과 거의 동일하다.
- header
typedef struct Item_s
{
const int nValue;
const char* lpszValue;
} Item;
- Go
func GotoCpp(items []Item) {
cItem := []C.Item{}
for _, item := items {
cStrValue := C.CString(item.StringValue)
defer C.free(unsafe.Pointer(cStrValue))
cNumValue := C.int(item.IntegerValue)
cItem = append(cItem, C.Item{
nValue: cNumValue,
lpszValue: cStrValue,
})
}
cPtrItems := (*C.Item)(unsafe.Pointer(&cItem[0]))
cItemLen := C.int(len(items))
C.GotoCpp(cPtritems, cItemLen)
}
- Cpp
int GotoCpp(const Item* arrItems, const int nItemLen) {
for ( int i = 0; i < nItemLen; i++ )
{
const int integerValue = arrItems[ i ].nValue;
const int stringValue = arrItems[ i ].lpszValue;
// Do Something...
}
}
CPP에서 Go로 배열을 넘기는 경우
Go에서 배열의 포인터를 넘기고, CPP에서 배열에 값을 할당하는 방식으로 연동해야 한다.
- header
typedef struct Item_s
{
const int nValue;
const char* lpszValue;
} Item;
- Go
func CppToGo() {
var cItems **C.Item
len := C.CppToGo(&cItems)
for _, item := range unsafe.Slice(cItems, len) {
integerValue := item.nValue
stringValue := C.GoString(v.lpszValue)
}
freeArr(cItems)
}
- Cpp
int CppToGo( Item** arrPtrItems[] ) {
int nLen = 5;
*arrPtrItems = (Item**) malloc( sizeof( Item* ) * nLen );
for ( int i = 0; i < nLen; i++ )
{
( *arrPtrItems )[ i ] = (Item*) malloc( sizeof( Item* ) );
( *arrPtrItems )[ i ]->nValue = MakeChar( "TEST" );
}
return nLen
}
// 메모리를 할당해 주어야 Go에서 접근이 가능하다.
char* MakeChar( const char* lpszData )
{
const auto nSize = strlen( lpszData );
char* lpszOut = (char*) malloc( nSize + 1 );
memcpy( lpszOut, lpszData, nSize * sizeof( char ) );
// Ensure null character
lpszOut[ nSize ] = '\0';
return lpszOut;
}