溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C++中怎么調(diào)用Golang方法

發(fā)布時(shí)間:2021-07-20 15:14:59 來(lái)源:億速云 閱讀:823 作者:Leah 欄目:編程語(yǔ)言

C++中怎么調(diào)用Golang方法,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。


現(xiàn)象

在一個(gè)APP技術(shù)項(xiàng)目中,子進(jìn)程按請(qǐng)求加載Go的ServiceModule,將需要拉起的ServiceModule信息傳遞給Go的Loader,存在C++調(diào)用Go方法,傳遞字符串的場(chǎng)景。

方案驗(yàn)證時(shí),發(fā)現(xiàn)有奇怪的將std::string對(duì)象的內(nèi)容傳遞給Go方法后,在Go方法協(xié)程中取到的值與預(yù)期不一致。

經(jīng)過(guò)一段時(shí)間的分析和驗(yàn)證,終于理解問(wèn)題產(chǎn)生的原因并給出解決方案,現(xiàn)分享如下。

背景知識(shí)

  1. Go有自己的內(nèi)存回收GC機(jī)制,通過(guò)make等申請(qǐng)的內(nèi)存不需要手動(dòng)釋放。

  2. C++中為std::string變量賦值新字符串后,.c_str()和.size()的結(jié)果會(huì)聯(lián)動(dòng)變化,尤其是.c_str()指向的地址也有可能變化。

  3. go build -buildmode=c-shared .生成的.h頭文件中定義了C++中Go的變量類型的定義映射關(guān)系,比如GoString、GoInt等。其中GoString實(shí)際是一個(gè)結(jié)構(gòu)體,包含一個(gè)字符指針和一個(gè)字符長(zhǎng)度。

原理及解釋

通過(guò)代碼示例方式解釋具體現(xiàn)象及原因,詳見(jiàn)注釋

C++側(cè)代碼:

	//
	// Created by w00526151 on 2020/11/5.
	//
	 
	#include <string>
	#include <iostream>
	#include <unistd.h>
	#include "libgoloader.h"
	 
	/**
	 * 構(gòu)造GoString結(jié)構(gòu)體對(duì)象
	 * @param p
	 * @param n
	 * @return
	 */
	GoString buildGoString(const char* p, size_t n){
	    //typedef struct { const char *p; ptrdiff_t n; } _GoString_;
	    //typedef _GoString_ GoString;
	    return {p, static_cast<ptrdiff_t>(n)};
	}
	 
	int main(){
	    std::cout<<"test send string to go in C++"<<std::endl;
	 
	    std::string tmpStr = "/tmp/udsgateway-netconftemplateservice";
	    printf("in C++ tmpStr: %p, tmpStr: %s, tmpStr.size:%lu \r\n", tmpStr.c_str(), tmpStr.c_str(), tmpStr.size());
	    {
	        //通過(guò)new新申請(qǐng)一段內(nèi)存做字符串拷貝
	        char *newStrPtr = NULL;
	        int newStrSize = tmpStr.size();
	        newStrPtr = new char[newStrSize];
	        tmpStr.copy(newStrPtr, newStrSize, 0);
	 
	        //調(diào)用Go方法,第一個(gè)參數(shù)直接傳std::string的c_str指針和大小,第二個(gè)參數(shù)傳在C++中單獨(dú)申請(qǐng)的內(nèi)存并拷貝的字符串指針,第三個(gè)參數(shù)和第一個(gè)一樣,但是在go代碼中做內(nèi)存拷貝保存。
	        //調(diào)用Go方法后,通過(guò)賦值修改std::string的值內(nèi)容,等待Go中新起的線程10s后再將三個(gè)參數(shù)值打印出來(lái)。
	        LoadModule(buildGoString(tmpStr.c_str(), tmpStr.size()), buildGoString(newStrPtr, newStrSize), buildGoString(tmpStr.c_str(),tmpStr.size()));
	        //修改tmpStr的值,tmpStr.c_str()得到的指針指向內(nèi)容會(huì)變化,tmpStr.size()的值也會(huì)變化,Go中第一個(gè)參數(shù)也會(huì)受到影響,前幾位會(huì)變成新字符串內(nèi)容。
	        //由于在Go中int是值拷貝,所以在Go中,第一個(gè)參數(shù)的長(zhǎng)度沒(méi)有變化,因此實(shí)際在Go中已經(jīng)出現(xiàn)內(nèi)存越界訪問(wèn),可能產(chǎn)生Coredump。
	        tmpStr = "new string";
	        printf("in C++ change tmpStr and delete newStrPtr, new tmpStr: %p, tmpStr: %s, tmpStr.size:%lu \r\n", tmpStr.c_str(), tmpStr.c_str(), tmpStr.size());
	        //釋放新申請(qǐng)的newStrPtr指針,Go中對(duì)應(yīng)第二個(gè)string變量?jī)?nèi)存也會(huì)受到影響,產(chǎn)生亂碼。
	        // 實(shí)際在Go中,已經(jīng)在訪問(wèn)一段在C++中已經(jīng)釋放的內(nèi)存,屬于野指針訪問(wèn),可能產(chǎn)生Coredump。
	        delete newStrPtr;
	    }
	    pause();
	}

Go側(cè)代碼:

	package main
	 
	import "C"
	import (
	    "fmt"
	    "time"
	)
	 
	func printInGo(p0 string, p1 string, p2 string){
	    time.Sleep(10 * time.Second)
	    fmt.Printf("in go function, p0:%s size %d, p1:%s size %d, p2:%s size %d", p0, len(p0), p1, len(p1), p2, len(p2))
	}
	 
	//export LoadModule
	func LoadModule(name string, version string, location string) int {
	    //通過(guò)make的方式,新構(gòu)建一段內(nèi)存來(lái)存放從C++處傳入的字符串,深度拷貝防止C++中修改影響Go
	    tmp3rdParam := make([]byte, len(location))
	    copy(tmp3rdParam, location)
	    new3rdParam := string(tmp3rdParam)
	    fmt.Println("in go loadModule,first param is",name,"second param is",version, "third param is", new3rdParam)
	    go printInGo(name, version, new3rdParam);
	    return 0
	}

Go側(cè)代碼通過(guò)-buildmode=c-shared的方式生成libgoloader.so及l(fā)ibgoloader.h供C++編譯運(yùn)行使用

	go build -o libgoloader.so -buildmode=c-shared .

程序執(zhí)行結(jié)果:

	test send string to go in C++
	in C++ tmpStr: 0x7fffe1fb93f0, tmpStr: /tmp/udsgateway-netconftemplateservice, tmpStr.size:38 
	# 將C++的指針傳給Go,一開(kāi)始打印都是OK的
	in go loadModule,first param is /tmp/udsgateway-netconftemplateservice second param is /tmp/udsgateway-netconftemplateservice third param is /tmp/udsgateway-netconftemplateservice
	# 在C++中,將指針指向的內(nèi)容修改,或者刪掉指針
	in C++ change tmpStr and delete newStrPtr, new tmpStr: 0x7fffe1fb93f0, tmpStr: new string, tmpStr.size:10 
	# 在Go中,參數(shù)1、參數(shù)2對(duì)應(yīng)的Go string變量都受到了影響,參數(shù)3由于做了深度拷貝,沒(méi)有受到影響。
	in go function, p0:new string eway-netconftemplateservice size 38, p1:        p???  netconftemplateservice size 38, p2:/tmp/udsgateway-netconftemplateservice size 38

關(guān)于C++中怎么調(diào)用Golang方法問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI