[1. 개요]
cgo 는 C code 를 호출하는 go package 의 생성을 가능하게 한다.
일반적으로 cgo 를 사용하기 위해 go 소스코드를 작성하는 방법은 pseudo 패키지 "C" 를 import 하는 것이다.
=> import "C"
CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS, LDFLAGS 등이 #cgo directive 내에 정의 될 수 있다.
pkg-config 로 관리 할 수도 있다.
gcc 가 사전에 PC 에 설치되어 있어야 한다.
go env 시 CC 와 CXX 가 각각 gcc 와 g++ 로 설정되어 있다.
[2. 예제]
package main | |
/* | |
#include <stdio.h> | |
struct Obj | |
{ | |
int a; | |
int b; | |
}; | |
int SetUp(int a, int b) | |
{ | |
struct Obj obj = {a, b}; | |
printf("Obj.a=%d, Obj.b=%d\n", obj.a, obj.b); | |
return obj.a + obj.b; | |
} | |
*/ | |
import "C" | |
import "fmt" | |
func main() { | |
a := C.int(3) | |
b := C.int(4) | |
ret := C.SetUp(a, b) | |
fmt.Println("ret", ret) | |
} |
위와 같이 go 소스코드에서 주석에 C 코드를 작성한다.
그리고 유의사항이 한가지 있는데,
C 코드를 감싸는 주석과 import "C" 는 반드시 붙어있어야 한다는 것이다.
[3. 라이브러리 링킹]
CFLAGS 와 LDFLAGS 를 통해 각각 include 파일 경로와 라이브러리 파일 경로를 명시 할 수 있다.
이를 이용하여 외부 C 라이브러리를 import 하여 사용 할 수 있다.
현재 소스코드 구조는 아래와 같다.

그리고 각 소스 파일은 아래와 같다
package main | |
/* | |
#cgo CFLAGS: -I "../lib/include" | |
#cgo LDFLAGS: -L "../lib/build2" -lcalc | |
#include "myheader.hpp" | |
int SetUp(int a, int b) | |
{ | |
return myadd(a, b); | |
} | |
*/ | |
import "C" | |
import "fmt" | |
func main() { | |
a := C.int(3) | |
b := C.int(4) | |
ret := C.SetUp(a, b) | |
fmt.Println("ret", ret) | |
} |
#include "myheader.hpp" | |
int myadd(int a, int b) | |
{ | |
return a*a + b*b; | |
} |
#ifndef MY_HEADER_HPP | |
#define MY_HEADER_HPP | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
int myadd(int a, int b); | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif |
윈도우의 경우 C 라이브러리는 가급적 msvc 로 빌드하지 말고 mingw 로 빌드하는 편이 좋다.
msvc 버전 차이일 수 있으나 Visual studio 2019 에서는 아래와 같이 출력된다.

cmake 파일 빌드 시 generator 를 MinGW Makefiles 로 명시해서 makefile 생성 후
mingw32-make 로 라이브러리 파일을 빌드하도록 한다.
이렇게 빌드한 라이브러리를 링킹하면 위와 같은 warning 은 출력되지 않는다.
[4. C++ 라이브러리와 링킹]
go 에서 c++ 표준 라이브러리 나 템플릿 같은 것은 직접적으로 사용 할 수 없어 보인다.
=> c 대비 cpp 만의의 feature 들 ex) namespace, template, class, ... 등을 사용하기 힘들어 보임
=> go env 등을 통해 cpp 관련 옵션등이 있는 것으로 보아 가능할 것 같으나, 방법을 따로 찾지 못함
그래서 보통 stl 등을 사용하는 코드를 작성하여 라이브러리로 빌드 후 go 와 링킹해서 사용하는 듯 하다.
물론 헤더파일은 C 언어스럽게 작성해야 한다.
package main | |
/* | |
#cgo CFLAGS: -I "../lib/include" | |
#cgo LDFLAGS: -L "../lib/build2" -lcalc -lstdc++ | |
#include "myheader.hpp" | |
int SetUp(int a, int b) | |
{ | |
return myadd(a, b); | |
} | |
*/ | |
import "C" | |
import "fmt" | |
func main() { | |
a := C.int(3) | |
b := C.int(4) | |
ret := C.SetUp(a, b) | |
fmt.Println("ret", ret) | |
} |
#include "myheader.hpp" | |
#include <iostream> | |
template <typename func> | |
void inner(func f) { | |
f(); | |
} | |
int myadd(int a, int b) | |
{ | |
auto A = a; | |
auto B = b; | |
const auto func = [&A, &B]() { | |
std::cout << "lambda" << std::endl; | |
std::cout << A << " " << B << '\n'; | |
A *= A; | |
B *= B; | |
}; | |
inner(func); | |
return A + B; | |
} |
위 예제 코드에서 cpp 코드는 cpp 만의 여러 특징들을 활용하여 작성해보았다.
auto, template, lambda 같은 것을 이용해서 작성하고
라이브러리로 빌드 후 go 와 링킹하였다.
여기서 중요한 점은 stdc++ 라이브러리를 함께 링킹해주어야 한다는 것이다.
주로 namespace std 를 사용하기 위해서 이며,
이 링킹 없이 go 소스 코드는 정상 동작하지 않는다.
undefined reference to `std::basic_ostream...' 같은 링킹 오류를 실제로 확인 할 수 있다,.
'Go' 카테고리의 다른 글
GO111MODULE (0) | 2022.08.10 |
---|---|
go 패키지 설치, 관리 (0) | 2022.08.10 |
ubuntu go version update (0) | 2022.04.06 |
Slice length, capacity and append, copy function (0) | 2022.02.08 |
Array & Slice (0) | 2022.02.07 |