C로부터의 Call Go 함수
C 프로그램(커널 모듈 등)과 인터페이스하기 위해 Go로 작성된 정적 객체를 작성하려고 합니다.
Go에서 C 함수를 호출하는 문서는 찾았지만, 그 반대로 가는 방법은 찾지 못했습니다.가능하지만 복잡하다는 것을 알게 되었습니다.
다음은 제가 발견한 것입니다.
이거 해본 사람 있어요?한마디로 Go로 작성된 PAM 모듈을 작성하려고 합니다.
C에서 Go 코드를 호출할 수 있습니다.하지만 그것은 혼란스러운 제안입니다.
이 과정은 링크한 블로그 투고에 개략적으로 설명되어 있습니다.하지만 그게 어떻게 큰 도움이 되지 않는지 알겠네요.여기 불필요한 비트가 없는 짧은 스니펫이 있습니다.그럼 좀 더 명확해질 거야
package foo
// extern int goCallbackHandler(int, int);
//
// static int doAdd(int a, int b) {
// return goCallbackHandler(a, b);
// }
import "C"
//export goCallbackHandler
func goCallbackHandler(a, b C.int) C.int {
return a + b
}
// This is the public function, callable from outside this package.
// It forwards the parameters to C.doAdd(), which in turn forwards
// them back to goCallbackHandler(). This one performs the addition
// and yields the result.
func MyAdd(a, b int) int {
return int( C.doAdd( C.int(a), C.int(b)) )
}
모든 것이 호출되는 순서는 다음과 같습니다.
foo.MyAdd(a, b) ->
C.doAdd(a, b) ->
C.goCallbackHandler(a, b) ->
foo.goCallbackHandler(a, b)
해야 할 한 점은 는 ""로 입니다.//export
과 바둑을 extern
입 、 C c c 하는 콜백은 합니다.즉, 사용하는 콜백은 패키지 내에서 정의해야 합니다.
할 수 의 커스텀핸들러 Go일 뿐)를이됩니다.void*
그 후, 콜백 핸들러에 의해서 패키지로 수신되어 호출됩니다.
현재 사용하고 있는 보다 고도의 예를 사용해 보겠습니다.이 경우 상당히 부하가 높은 작업을 수행하는 C 함수가 있습니다.USB 장치에서 파일 목록을 읽습니다.이 작업은 시간이 걸릴 수 있으므로 진행 상황을 앱에 알려주시기 바랍니다.프로그램에서 정의한 함수 포인터를 전달함으로써 이를 수행할 수 있습니다.호출될 때마다 사용자에게 몇 가지 진행률 정보를 표시할 뿐입니다.잘 알려진 시그니처가 있기 때문에 독자적인 타입을 할당할 수 있습니다.
type ProgressHandler func(current, total uint64, userdata interface{}) int
이 핸들러는 일부 진행 정보(현재 수신된 파일 수 및 총 파일 수)와 사용자가 원하는 모든 정보를 유지할 수 있는 인터페이스{} 값을 가져옵니다.
이제 이 핸들러를 사용할 수 있도록 C와 Go 배관을 작성해야 합니다.다행히 내가 도서관에서 호출하고 싶은 C 함수는 우리가 유형의 사용자 데이터 구조를 전달할 수 있게 해준다.void*
즉, 원하는 것은 무엇이든 담을 수 있습니다.질문 없이 그대로 바둑으로 되돌릴 수 있습니다.이 모든 작업을 수행하기 위해 Go에서 라이브러리 함수를 직접 호출하지는 않지만 이름을 붙일 C 래퍼를 만듭니다.goGetFiles()
이 래퍼에서는 실제로 사용자 데이터 오브젝트와 함께 Go 콜백을 C 라이브러리에 공급합니다.
package foo
// #include <somelib.h>
// extern int goProgressCB(uint64_t current, uint64_t total, void* userdata);
//
// static int goGetFiles(some_t* handle, void* userdata) {
// return somelib_get_files(handle, goProgressCB, userdata);
// }
import "C"
import "unsafe"
주의:goGetFiles()
함수는 콜백의 함수 포인터를 파라미터로 사용하지 않습니다.대신 사용자가 제공한 콜백은 해당 핸들러와 사용자 자신의 사용자 데이터 값을 모두 유지하는 커스텀 구조로 패킹됩니다.이것을 다음에 전달합니다.goGetFiles()
userdata 파라미터로 지정합니다.
// This defines the signature of our user's progress handler,
type ProgressHandler func(current, total uint64, userdata interface{}) int
// This is an internal type which will pack the users callback function and userdata.
// It is an instance of this type that we will actually be sending to the C code.
type progressRequest struct {
f ProgressHandler // The user's function pointer
d interface{} // The user's userdata.
}
//export goProgressCB
func goProgressCB(current, total C.uint64_t, userdata unsafe.Pointer) C.int {
// This is the function called from the C world by our expensive
// C.somelib_get_files() function. The userdata value contains an instance
// of *progressRequest, We unpack it and use it's values to call the
// actual function that our user supplied.
req := (*progressRequest)(userdata)
// Call req.f with our parameters and the user's own userdata value.
return C.int( req.f( uint64(current), uint64(total), req.d ) )
}
// This is our public function, which is called by the user and
// takes a handle to something our C lib needs, a function pointer
// and optionally some user defined data structure. Whatever it may be.
func GetFiles(h *Handle, pf ProgressFunc, userdata interface{}) int {
// Instead of calling the external C library directly, we call our C wrapper.
// We pass it the handle and an instance of progressRequest.
req := unsafe.Pointer(&progressequest{ pf, userdata })
return int(C.goGetFiles( (*C.some_t)(h), req ))
}
C 바인딩은 여기까지입니다.이제 사용자 코드는 매우 간단합니다.
package main
import (
"foo"
"fmt"
)
func main() {
handle := SomeInitStuff()
// We call GetFiles. Pass it our progress handler and some
// arbitrary userdata (could just as well be nil).
ret := foo.GetFiles( handle, myProgress, "Callbacks rock!" )
....
}
// This is our progress handler. Do something useful like display.
// progress percentage.
func myProgress(current, total uint64, userdata interface{}) int {
fc := float64(current)
ft := float64(total) * 0.01
// print how far along we are.
// eg: 500 / 1000 (50.00%)
// For good measure, prefix it with our userdata value, which
// we supplied as "Callbacks rock!".
fmt.Printf("%s: %d / %d (%3.2f%%)\n", userdata.(string), current, total, fc / ft)
return 0
}
이 모든 것이 실제보다 훨씬 더 복잡해 보입니다.콜 순서는 이전 예와 달리 변경되지 않았지만 체인의 마지막에2개의 추가 콜이 수신됩니다.
순서는 다음과 같습니다.
foo.GetFiles(....) ->
C.goGetFiles(...) ->
C.somelib_get_files(..) ->
C.goProgressCB(...) ->
foo.goProgressCB(...) ->
main.myProgress(...)
Go 1.5 릴리즈에 따라 답변이 변경되었습니다.
얼마 전에 질문한 SO 질문은 1.5의 추가 기능에 비추어 다시 문제를 해결합니다.
gccgo를 사용하면 혼란스러운 제안이 아닙니다.이 기능은 다음과 같습니다.
푸고
package main
func Add(a, b int) int {
return a + b
}
바
#include <stdio.h>
extern int go_add(int, int) __asm__ ("example.main.Add");
int main() {
int x = go_add(2, 3);
printf("Result: %d\n", x);
}
파일 만들기
all: main
main: foo.o bar.c
gcc foo.o bar.c -o main
foo.o: foo.go
gccgo -c foo.go -o foo.o -fgo-prefix=example
clean:
rm -f main *.o
내가 생각하는 한, 그것은 불가능하다.
주의: 내보내기를 사용하는 경우 프리암블에 C 함수를 정의할 수 없습니다.
출처 : https://github.com/golang/go/wiki/cgo
언급URL : https://stackoverflow.com/questions/6125683/call-go-functions-from-c
'programing' 카테고리의 다른 글
OpenMP:지역 변수를 자동으로 개인적인 거? (0) | 2022.08.02 |
---|---|
Vue.js: 생산성을 높이기 위해 먼저 스크립트 또는 템플릿을 단일 파일 컴포넌트에 넣어야 합니까? (0) | 2022.08.02 |
여러 @Suppress Warnings 주석 조합 - Eclipse Indigo (0) | 2022.08.02 |
Vuej에 대한 성능 메트릭 (0) | 2022.08.02 |
Java를 사용하여 파일 이름 변경 (0) | 2022.08.01 |