網上沒有完整的例子,這里是本人參考各個代碼repo總結的結果。遺憾的是,我沒有實現Pure Go的庫。調用該lib的GO程序需要和wrap.cxx,還有.h文件一起build產生可執行文件。本文采用go 1.15版本, SWIG 4.0.1 cmake 3.17.1
本文的代碼參考https://github.com/zacg/simplelib. 不同的是,原例子並沒有使用CMake,也沒有產生lib文件,而是手動輸入swig和go命令,將所有c++代碼和go代碼混在一起完成了go對c++的調用。這在實際工程應用中是很不方便的。我們希望C++寫的部分編譯成一個lib,GO調用時只需要調用這個lib就可以,而不需要連lib一起重新編譯。因此我對這個例子做了改動,加入了CMake,並且將執行程序和動態庫分開編譯。
下面是我修改后的代碼目錄結構,本文使用默認GOPATH = ~/go:
xian@chaos:~/go/src/simplelib$ tree
.
├── CMakeLists.txt
├── include
│ └── simpleclass.h
├── lib.go
├── simplelib.i
└── src
└── simpleclass.cpp
2 directories, 5 files
此外還有個調用代碼main.go,他可以在任何地方。
首先看一下c++/go代碼
simpleclass.h
#ifndef SIMPLECLASS_H
#define SIMPLECLASS_H
#include <iostream>
#include <vector>
class SimpleClass
{
public:
SimpleClass(){};
std::string hello();
void helloString(std::vector<std::string> *results);
void helloBytes(std::vector<char> *results);
};
#endif
simpleclass.cpp
#include "simpleclass.h"
std::string SimpleClass::hello(){
return "world";
}
void SimpleClass::helloString(std::vector<std::string> *results){
results->push_back("world");
}
void SimpleClass::helloBytes(std::vector<char> *results){
results->push_back('w');
results->push_back('o');
results->push_back('r');
results->push_back('l');
results->push_back('d');
}
lib.go
package simplelib
/*
#cgo LDFLAGS: -L. -lExample
*/
import "C"
simplelib.i
%module simplelib
%{
#include "simpleclass.h"
%}
%include <typemaps.i>
%include "std_string.i"
%include "std_vector.i"
// This will create 2 wrapped types in Go called
// "StringVector" and "ByteVector" for their respective
// types.
namespace std {
%template(StringVector) vector<string>;
%template(ByteVector) vector<char>;
}
%include "simpleclass.h"
main.go
package main
import (
"fmt"
"simplelib/build/go/Example"
//這是swig 產生wrapper文件的目錄
)
func main() {
simpleClass := simplelib.NewSimpleClass()
result := simpleClass.Hello()
fmt.Println(result)
}
CMakeLists.txt
分三個部分:第一部分與go無關,只需要.h .cpp生成動態庫libExample.so。第二部分使用swig_add_library,根據.i文件以及.i文件中涉及到的.h文件生成wrap.cxx文件。第三部分則是復制一些文件,包括libExample.so到特定目錄以解決go build時的路徑問題。
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0 FATAL_ERROR)
find_package(SWIG REQUIRED)
include(UseSWIG)
find_program(GO_EXECUTABLE go REQUIRED)
if(POLICY CMP0078)
cmake_policy(SET CMP0078 NEW)
endif()
project(
Example
LANGUAGES CXX
)
#########################################################
# 第一步產生Example.so,這里只需要.cpp .h文件
#########################################################
add_library(Example SHARED "")
target_sources(Example
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/simpleclass.h>
$<INSTALL_INTERFACE:include/simpleclass.h>
PRIVATE
src/simpleclass.cpp
)
target_include_directories(Example
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_features(Example PUBLIC cxx_std_11)
add_library(${PROJECT_NAME}::Example ALIAS Example)
############################################################
## 第二步產生wrap.cxx文件,這里只需要*.i及其include的頭文件
###########################################################
set(CMAKE_SWIG_FLAGS -cgo -intgosize 64)
set_property(SOURCE simplelib.i PROPERTY CPLUSPLUS ON) #沒有這一條會生成wrap.c
set_property(SOURCE simplelib.i PROPERTY COMPILE_OPTIONS
-package simplelib)
swig_add_library(exampleForGo
LANGUAGE go
TYPE OBJECT
OUTPUT_DIR ${PROJECT_BINARY_DIR}/go/${PROJECT_NAME}
SOURCES simplelib.i)
target_include_directories(exampleForGo PRIVATE ${PROJECT_SOURCE_DIR}/include)
#simplelib.i中有include “*.h”, 這里為這些頭文件提供路徑
set_target_properties(exampleForGo PROPERTIES
SWIG_INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/include #SWIG也添加include路徑
SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON
POSITION_INDEPENDENT_CODE ON)
target_link_libraries(exampleForGo PRIVATE ${PROJECT_NAME}::Example)
#############################################################################
# 第三步復制一些文件到wrapper所在目錄,供go build使用
#############################################################################
file(COPY "${CMAKE_SOURCE_DIR}/include/simpleclass.h" DESTINATION "${PROJECT_BINARY_DIR}/go/${PROJECT_NAME}")
file(COPY "${CMAKE_SOURCE_DIR}/lib.go" DESTINATION "${PROJECT_BINARY_DIR}/go/${PROJECT_NAME}")
#############################################################################
# 復制動態鏈接庫libExample.so到系統目錄 /usr/local/lib
#############################################################################
install(TARGETS Example DESTINATION ${CMAKE_INSTALL_LIBDIR})
好了。接下來進入到CMakeLists.txt開始build
cmake -Bbuild
sudo cmake --build build --target install #這里需要sudo權限訪問/usr/local/lib
這樣就算是安裝好庫了。接下來看怎么調用它。進入main.go所在目錄,
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib #把庫目錄加入到環境變量
go build main.go #build
./main #run!
world #<==you should read this
最后,我好像沒找到go的打包程序,看來只能使用CPack了。