C++调用Golang

C++调用Golang

安装配置golang

brew install golang
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=on
# 安装gomobile
go install golang.org/x/mobile/cmd/gomobile@latest
echo "export PATH=\$(go env GOPATH)/bin:\$PATH" >> ~/.zshrc

编译可执行文件

testgo.go

package main
import "fmt"
func main() {
	fmt.Println("hello1")
}

testgo.plist

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>platform-application</key>
	<true/>
</dict>
</plist>
env GOOS=darwin GOARCH=arm64 go build -ldflags '-s -w' -tags=ios testgo.go
ldid -Stestgo.plist testgo
#在设备上执行

注意: ldflags为去除dwarf信息,不加该项编译执行时会报如下错误:

dyld: malformed mach-o image: segment load command __DWARF filesize (0x7AB3C) is larger than vmsize (0x0)
Abort trap: 6

编译framework

testgolib.go

package testgolib
import "C"
func AddInt(a, b int) int {
	return a + b
}
func AddStr(a, b string) string {
	return a + b 
}

main.mm

#import "Testgolib/Testgolib.h"
int main (int argc, const char * argv[]) {
    NSLog(@"r1=%ld r2=%@", TestgolibAddInt(1, 2), TestgolibAddStr(@"1", @"2"));
	return 0;
}
go mod init testgolib
go get golang.org/x/mobile/cmd/gobind
gomobile bind -target ios -o testgolib.xcframework testgolib

注意: 此方法需要xcode-select设置为正确的Xcode版本,笔者使用XCode12 注意: gomobile要求包名不能是main且不能有main函数,需要导出的函数首字母须大写

编译静态库

gomobile是简化编译iOS/Android的工具链,但本人今天研究之下发现其在Mac下只能产生动态库, 因此又研究了一下如何用Golang编译iOS静态库

testgolib.go

package main
import "C"
/*
int testfunc();
*/
//export AddInt
func AddInt(a, b int) int {
	return a + b
}
//export AddStr
func AddStr(a, b string) *C.char { // cgo不支持返回go指针
	c := a + b
	return C.CString(c)
}
//export TestFunc
func TestFunc() C.int {
	return C.testfunc()
}
func main() {
}

main.mm

#include <iostream>
#include <string>
extern "C" {
#include "testgolib.h"
}
static GoString stdstr_to_gostr(const std::string& s) {
    return {s.c_str(), (ptrdiff_t)s.length()};
}
int testfunc() {
    return 123;
}
int main (int argc, const char * argv[]) {
    int r1 = (int)AddInt(1,2);
    std::string r2 = AddStr(stdstr_to_gostr("1"), stdstr_to_gostr("2"));
    std::cout << "r1=" << r1 << " r2=" << r2 << std::endl; // C++调用Go
    std::cout << "r3=" << (int)TestFunc() << std::endl; // Go调用C
    return 0;
}
export GOROOT=$(go env GOROOT)
# CGO_ENABLED=1 GOOS=ios GOARCH=arm64 CC="xcrun -sdk iphoneos clang" CXX="xcrun -sdk iphoneos clang" \
CGO_ENABLED=1 GOOS=ios GOARCH=arm64 CC="$GOROOT/misc/ios/clangwrap.sh" CXX="$GOROOT/misc/ios/clangwrap.sh" \
CGO_CFLAGS="-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=9.0 -fembed-bitcode -arch arm64" \
CGO_CXXFLAGS="-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=9.0 -fembed-bitcode -arch arm64" \
CGO_LDFLAGS="-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=9.0 -fembed-bitcode -arch arm64" \
go build -buildmode=c-archive -o testgolib.a testgolib.go

注意: 正常情况cgo会生成.a和.h文件 注意: cgo导出函数必须指定注释"//export",需要有main函数且是main包 注意: cgo不支持返回string类型,所以转而用C.char,否则报错

goroutine 17 [running, locked to thread]:
panic({0x10052a6c0, 0x13001a290})
	/usr/local/Cellar/go/1.19.3/libexec/src/runtime/panic.go:987 +0x3c0
runtime.cgoCheckArg(0x100527a80, 0x13001a280, 0x10?, 0x0, {0x10050295b, 0x19})
	/usr/local/Cellar/go/1.19.3/libexec/src/runtime/cgocall.go:524 +0x308
...

编译release

只需要指定ldflags

go build -ldflags "-s -w"