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;
}