如何在Golang中使用C++

如何在Golang中使用C++ 有一个开源库:https://github.com/infinispan/cpp-client,我需要用 Go 语言为其编写一个包装器。 该库包含“C 头文件”,并且库本身是用 C++ 编写的。

我不想使用“SWIG”。

我使用的是 Windows 10 操作系统,并已安装 gcc 和 g++。

2 回复

无法直接从Go调用C++代码。如果你不想使用SWIG,那么你需要编写自己的C函数来封装你需要调用的C++函数,然后从Go调用这些C函数。这个例子在Go Playground上无法运行,因为它似乎不支持cgo(或者我只是不知道如何启用它),但如果你将其保存到计算机上并分离文件,它是可以工作的:https://play.golang.org/p/bBoAxJd1eZf

func main() {
    fmt.Println("hello world")
}

更多关于如何在Golang中使用C++的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中调用C++代码可以通过C语言层进行封装。以下是具体实现步骤:

1. 创建C语言包装头文件

// wrapper.h
#ifdef __cplusplus
extern "C" {
#endif

// 导出C接口函数
void* create_client(const char* host, int port);
void destroy_client(void* client);
int put_data(void* client, const char* key, const char* value);
char* get_data(void* client, const char* key);
int remove_data(void* client, const char* key);

#ifdef __cplusplus
}
#endif

2. 实现C++包装层

// wrapper.cpp
#include "wrapper.h"
#include "infinispan/hotrod/ConfigurationBuilder.h"
#include "infinispan/hotrod/RemoteCacheManager.h"
#include "infinispan/hotrod/RemoteCache.h"

using namespace infinispan::hotrod;

extern "C" {

void* create_client(const char* host, int port) {
    ConfigurationBuilder builder;
    builder.addServer().host(host).port(port);
    RemoteCacheManager* cacheManager = new RemoteCacheManager(builder.build(), false);
    cacheManager->start();
    return static_cast<void*>(cacheManager);
}

void destroy_client(void* client) {
    RemoteCacheManager* cacheManager = static_cast<RemoteCacheManager*>(client);
    cacheManager->stop();
    delete cacheManager;
}

int put_data(void* client, const char* key, const char* value) {
    try {
        RemoteCacheManager* cacheManager = static_cast<RemoteCacheManager*>(client);
        RemoteCache<std::string, std::string> cache = cacheManager->getCache<std::string, std::string>();
        cache.put(key, value);
        return 0;
    } catch (...) {
        return -1;
    }
}

char* get_data(void* client, const char* key) {
    try {
        RemoteCacheManager* cacheManager = static_cast<RemoteCacheManager*>(client);
        RemoteCache<std::string, std::string> cache = cacheManager->getCache<std::string, std::string>();
        std::string value = cache.get(key);
        
        char* result = new char[value.length() + 1];
        strcpy(result, value.c_str());
        return result;
    } catch (...) {
        return nullptr;
    }
}

int remove_data(void* client, const char* key) {
    try {
        RemoteCacheManager* cacheManager = static_cast<RemoteCacheManager*>(client);
        RemoteCache<std::string, std::string> cache = cacheManager->getCache<std::string, std::string>();
        cache.remove(key);
        return 0;
    } catch (...) {
        return -1;
    }
}

}

3. Go语言调用层

// infinispan.go
package infinispan

/*
#cgo CXXFLAGS: -std=c++11
#cgo LDFLAGS: -L${SRCDIR}/lib -lcppclient -lhotrod-client -lssl -lcrypto -lpthread

#include "wrapper.h"
#include <stdlib.h>
*/
import "C"
import (
    "unsafe"
)

type Client struct {
    ptr unsafe.Pointer
}

func NewClient(host string, port int) *Client {
    chost := C.CString(host)
    defer C.free(unsafe.Pointer(chost))
    
    ptr := C.create_client(chost, C.int(port))
    return &Client{ptr: ptr}
}

func (c *Client) Close() {
    C.destroy_client(c.ptr)
}

func (c *Client) Put(key, value string) error {
    ckey := C.CString(key)
    cvalue := C.CString(value)
    defer C.free(unsafe.Pointer(ckey))
    defer C.free(unsafe.Pointer(cvalue))
    
    result := C.put_data(c.ptr, ckey, cvalue)
    if result != 0 {
        return errors.New("put operation failed")
    }
    return nil
}

func (c *Client) Get(key string) (string, error) {
    ckey := C.CString(key)
    defer C.free(unsafe.Pointer(ckey))
    
    cvalue := C.get_data(c.ptr, ckey)
    if cvalue == nil {
        return "", errors.New("get operation failed")
    }
    defer C.free(unsafe.Pointer(cvalue))
    
    return C.GoString(cvalue), nil
}

func (c *Client) Remove(key string) error {
    ckey := C.CString(key)
    defer C.free(unsafe.Pointer(ckey))
    
    result := C.remove_data(c.ptr, ckey)
    if result != 0 {
        return errors.New("remove operation failed")
    }
    return nil
}

4. 编译和链接

创建Makefile或构建脚本:

# Makefile
CXX = g++
GO = go
CXXFLAGS = -std=c++11 -fPIC -I/path/to/infinispan/include
LDFLAGS = -shared -L/path/to/infinispan/lib -lhotrod-client -lssl -lcrypto -lpthread

all: libwrapper.so wrapper.a

libwrapper.so: wrapper.cpp wrapper.h
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ wrapper.cpp

wrapper.a: wrapper.cpp wrapper.h
    $(CXX) $(CXXFLAGS) -c wrapper.cpp -o wrapper.o
    ar rcs $@ wrapper.o

clean:
    rm -f *.o *.so *.a

5. 使用示例

// main.go
package main

import (
    "fmt"
    "log"
    "./infinispan"
)

func main() {
    client := infinispan.NewClient("localhost", 11222)
    defer client.Close()
    
    err := client.Put("key1", "value1")
    if err != nil {
        log.Fatal(err)
    }
    
    value, err := client.Get("key1")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Got value: %s\n", value)
    
    err = client.Remove("key1")
    if err != nil {
        log.Fatal(err)
    }
}

关键注意事项:

  1. 内存管理:C++分配的内存需要在Go中正确释放
  2. 异常处理:C++异常需要转换为C错误码
  3. 类型转换:注意Go字符串与C字符串的转换
  4. 线程安全:确保C++客户端是线程安全的
  5. 编译依赖:需要正确链接所有C++库依赖

这种方法通过C语言层封装C++接口,避免了Go直接调用C++的复杂性,同时保持了类型安全和内存管理的可控性。

回到顶部