반응형

다른 언어에서 Go 함수 호출 (C언어 .so 파일 이용)

 

[출처] https://github.com/vladimirvivien/go-cshared-examples

GitHub - vladimirvivien/go-cshared-examples: Calling Go Functions from Other Languages using C Shared Libraries

Calling Go Functions from Other Languages using C Shared Libraries - GitHub - vladimirvivien/go-cshared-examples: Calling Go Functions from Other Languages using C Shared Libraries

github.com

 

C 공유 라이브러리를 사용하여 다른 언어에서 Go 함수 호출

 

저장소에는 다른 언어에서 Go 함수 호출 (medium.com) 기사에 대한 소스 예제가 포함되어 있습니다. 컴파일러는 -buildmode=c-shared 빌드 플래그를 사용하여 Go 함수를 C 스타일 API로 노출하는 표준 공유 객체 바이너리 파일(.so)을 출력합니다. 이를 통해 프로그래머는 이 저장소에서 수행한 것처럼 C, Python, Ruby, Node, Java, Lua를 비롯한 다른 언어에서 호출할 수 있는 Go 라이브러리를 만들 수 있습니다.

 

Go 코드 작성

 

먼저 Go 코드를 작성해 보겠습니다. 다른 언어에서도 사용할 수 있도록 Go 라이브러리를 작성했다고 가정해 보겠습니다. 코드를 공유 라이브러리로 컴파일하기 전에 따라야 할 네 가지 요구 사항이 있습니다.

 

- 패키지는 기본 패키지여야 합니다. 컴파일러는 패키지와 모든 종속성을 단일 공유 오브젝트 바이너리로 빌드합니다.

- 소스는 의사 패키지 "C"를 가져와야 합니다.

- //export 주석을 사용하여 다른 언어에서 액세스할 수 있도록 하려는 기능에 주석을 답니다.

- 빈 기본 함수를 선언해야 합니다.

 

다음 Go 소스는 Add, Cosine, Sort, Log 함수를 내보냅니다.

 

파일 awesome.go

 

package main

 

import "C"

 

import (

"fmt"

"math"

"sort"

"sync"

)

 

var count int

var mtx sync.Mutex

 

//export Add

func Add(a, b int) int {

return a + b

}

 

//export Cosine

func Cosine(x float64) float64 {

return math.Cos(x)

}

 

//export Sort

func Sort(vals []int) {

sort.Ints(vals)

}

 

//export Log

func Log(msg string) int {

mtx.Lock()

defer mtx.Unlock()

fmt.Println(msg)

count++

return count

}

 

func main() {}

 

패키지는 공유 오브젝트 바이너리를 생성하기 위해 -buildmode=c-shared 빌드 플래그를 사용하여 컴파일됩니다.

 

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

 

완료되면 컴파일러는 아래와 같이 awesome.so와 awesome.h, 즉 C 헤더 파일과 공유 오브젝트 파일을 출력합니다.

 

-rw-rw-r — 1362 Feb 11 07:59 awesome.h

-rw-rw-r — 1997880 Feb 11 07:59 awesome.so

 

파일 크기

 

.so는 약 2Mb로 작은 라이브러리에 비해 상대적으로 큽니다. 이는 전체 Go 런타임 시스템과 종속 패키지가 단일 공유 오브젝트 바이너리에 압축되어 있기 때문입니다(단일 정적 실행 가능 바이너리를 컴파일하는 것과 유사).

 

헤더 파일

 

헤더 파일은 cgo 형식을 사용하여 Go 호환 유형에 매핑된 C 유형을 정의합니다.

 

/* Created by “go tool cgo” — DO NOT EDIT. */

...

typedef signed char GoInt8;

typedef unsigned char GoUint8;

typedef short GoInt16;

typedef unsigned short GoUint16;

typedef int GoInt32;

typedef unsigned int GoUint32;

typedef long long GoInt64;

typedef unsigned long long GoUint64;

typedef GoInt64 GoInt;

typedef GoUint64 GoUint;

typedef __SIZE_TYPE__ GoUintptr;

typedef float GoFloat32;

typedef double GoFloat64;

typedef float _Complex GoComplex64;

typedef double _Complex GoComplex128;

 

/*

static assertion to make sure the file is being used on architecture

at least with matching size of GoInt.

*/

typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

 

typedef struct { const char *p; GoInt n; } GoString;

typedef void *GoMap;

typedef void *GoChan;

typedef struct { void *t; void *v; } GoInterface;

typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

#endif

 

/* End of boilerplate cgo prologue. */

 

#ifdef __cplusplus

extern "C" {

#endif

 

extern GoInt Add(GoInt p0, GoInt p1);

extern GoFloat64 Cosine(GoFloat64 p0);

extern void Sort(GoSlice p0);

extern GoInt Log(GoString p0);

 

#ifdef __cplusplus

}

#endif

 

공유 객체 파일

 

컴파일러에 의해 생성된 다른 파일은 64비트 ELF 공유 오브젝트 바이너리 파일입니다. 명령을 사용하여 해당 정보를 확인할 수 있습니다.

 

$> file awesome.so

awesome.so: ELF 64-bit LSB shared object, x86–64, version 1 (SYSV), dynamically linked, BuildID[sha1]=1fcf29a2779a335371f17219fffbdc47b2ed378a, not stripped

 

nm 명령을 사용하면 grepGo 기능이 공유 객체 파일로 내보내졌는지 확인할 수 있습니다.

 

$> nm awesome.so | grep -e "T Add" -e "T Cosine" -e "T Sort" -e "T Log"

00000000000d0db0 T Add

00000000000d0e30 T Cosine

00000000000d0f30 T Log

00000000000d0eb0 T Sort

 

C 언어에서 호출

 

공유 오브젝트 라이브러리를 사용하여 C에서 Go 함수를 호출하는 방법에는 두 가지가 있습니다. 첫째, 컴파일 시 공유 라이브러리를 정적으로 바인딩할 수 있지만 런타임에는 동적으로 연결할 수 있습니다. 또는 Go 함수 기호가 런타임에 동적으로 로드되고 바인딩되도록 합니다.

 

동적 연결

 

이 접근 방식에서는 헤더 파일을 사용하여 공유 객체 파일에서 내보낸 유형과 함수를 정적으로 참조합니다. 코드는 아래와 같습니다.

 

파일 client1.c

 

#include <stdio.h>

#include "awesome.h"

 

int main() {

printf("Using awesome lib from C:\n");

 

//Call Add() - passing integer params, interger result

GoInt a = 12;

GoInt b = 99;

printf("awesome.Add(12,99) = %d\n", Add(a, b));

 

//Call Cosine() - passing float param, float returned

printf("awesome.Cosine(1) = %f\n", (float)(Cosine(1.0)));

 

//Call Sort() - passing an array pointer

GoInt data[6] = {77, 12, 5, 99, 28, 23};

GoSlice nums = {data, 6, 6};

Sort(nums);

printf("awesome.Sort(77,12,5,99,28,23): ");

for (int i = 0; i < 6; i++){

printf("%d,", ((GoInt *)nums.data)[i]);

}

printf("\n");

 

//Call Log() - passing string value

GoString msg = {"Hello from C!", 13};

Log(msg);

}

 

다음으로 공유 오브젝트 라이브러리를 지정하여 C 코드를 컴파일합니다.

 

$> gcc -o client client1.c ./awesome.so

 

결과 바이너리가 실행되면 awesome.so 라이브러리에 연결되어 아래 출력과 같이 Go에서 내보낸 함수를 호출합니다.

 

$> ./client

awesome.Add(12,99) = 111

awesome.Cosine(1) = 0.540302

awesome.Sort(77,12,5,99,28,23): 5,12,23,28,77,99,

Hello from C!

 

동적 로드

 

이 접근 방식에서 C 코드는 동적 링크 로더 라이브러리(libdl.so)를 사용하여 내보낸 기호를 동적으로 로드하고 바인딩합니다. 라이브러리 파일 열기, 기호 조회, 오류 검색, 공유 라이브러리 파일 닫기 등 dhfcn.h에 정의된 함수를 사용합니다.

 

바인딩 및 연결은 소스 코드에서 수행되므로 이 버전은 더 깁니다. 그러나 다음 코드에서 강조된 것처럼 이전과 동일한 작업을 수행합니다.

 

파일 client2.c

 

#include <stdlib.h>

#include <stdio.h>

#include <dlfcn.h>

 

// define types needed

typedef long long go_int;

typedef double go_float64;

typedef struct{void *arr; go_int len; go_int cap;} go_slice;

typedef struct{const char *p; go_int len;} go_str;

 

int main(int argc, char **argv) {

void *handle;

char *error;

 

// use dlopen to load shared object

handle = dlopen ("./awesome.so", RTLD_LAZY);

if (!handle) {

fputs (dlerror(), stderr);

exit(1);

}

 

// resolve Add symbol and assign to fn ptr

go_int (*add)(go_int, go_int) = dlsym(handle, "Add");

if ((error = dlerror()) != NULL) {

fputs(error, stderr);

exit(1);

}

// call Add()

go_int sum = (*add)(12, 99);

printf("awesome.Add(12, 99) = %d\n", sum);

 

// resolve Cosine symbol

go_float64 (*cosine)(go_float64) = dlsym(handle, "Cosine");

if ((error = dlerror()) != NULL) {

fputs(error, stderr);

exit(1);

}

// Call Cosine

go_float64 cos = (*cosine)(1.0);

printf("awesome.Cosine(1) = %f\n", cos);

 

// resolve Sort symbol

void (*sort)(go_slice) = dlsym(handle, "Sort");

if ((error = dlerror()) != NULL) {

fputs(error, stderr);

exit(1);

}

// call Sort

go_int data[5] = {44,23,7,66,2};

go_slice nums = {data, 5, 5};

sort(nums);

printf("awesome.Sort(44,23,7,66,2): ");

for (int i = 0; i < 5; i++){

printf("%d,", ((go_int *)data)[i]);

}

printf("\n");

 

// resolve Log symbol

go_int (*log)(go_str) = dlsym(handle, "Log");

if ((error = dlerror()) != NULL) {

fputs(error, stderr);

exit(1);

}

// call Log

go_str msg = {"Hello from C!", 13};

log(msg);

 

// close file handle when done

dlclose(handle);

}

 

이전 코드에서는 Go 호환 C 타입 go_int, go_float, go_slice 및 go_str의 자체 하위 집합을 정의했습니다. dlsym를 로드 하고 해당 함수 포인터에 할당하는 데 사용합니다. 다음으로 다음과 같이 라이브러리(awesome.so 아님)와 연결하는 코드를 컴파일합니다.

 

$> gcc -o client client2.c -ldl

 

코드가 실행되면 C 바이너리가 로드되고 공유 라이브러리 awesome.so에 연결되어 다음 출력이 생성됩니다.

 

$> ./client

awesome.Add(12, 99) = 111

awesome.Cosine(1) = 0.540302

awesome.Sort(44,23,7,66,2): 2,7,23,44,66,

Hello from C!

 

파이썬에서 호출

 

Python에서는 더 쉬워집니다. 다음 예제처럼 외부 함수 라이브러리 ctypes를 사용하여 awesome.so 공유 라이브러리에서 Go 함수를 호출할 수 있습니다.

 

파일 client.py

 

from __future__ import print_function

from ctypes import *

 

lib = cdll.LoadLibrary("./awesome.so")

 

# describe and invoke Add()

lib.Add.argtypes = [c_longlong, c_longlong]

lib.Add.restype = c_longlong

print("awesome.Add(12,99) = %d" % lib.Add(12,99))

 

# describe and invoke Cosine()

lib.Cosine.argtypes = [c_double]

lib.Cosine.restype = c_double

print("awesome.Cosine(1) = %f" % lib.Cosine(1))

 

# define class GoSlice to map to:

# C type struct { void *data; GoInt len; GoInt cap; }

class GoSlice(Structure):

_fields_ = [("data", POINTER(c_void_p)), ("len", c_longlong), ("cap", c_longlong)]

 

nums = GoSlice((c_void_p * 5)(74, 4, 122, 9, 12), 5, 5)

 

# call Sort

lib.Sort.argtypes = [GoSlice]

lib.Sort.restype = None

lib.Sort(nums)

print("awesome.Sort(74,4,122,9,12) = %s" % [nums.data[i] for i in range(nums.len)])

 

# define class GoString to map:

# C type struct { const char *p; GoInt n; }

class GoString(Structure):

_fields_ = [("p", c_char_p), ("n", c_longlong)]

 

# describe and call Log()

lib.Log.argtypes = [GoString]

lib.Log.restype = c_longlong

msg = GoString(b"Hello Python!", 13)

print("log id %d"% lib.Log(msg))

 

변수 lib는 공유 오브젝트 파일에서 로드된 기호를 나타냅니다. 또한 Python 클래스를 정의하고 해당 C 구조체 유형에 매핑했습니다. Python 코드가 실행되면 공유 오브젝트에서 Go 함수를 호출하여 다음 출력을 생성합니다.

 

$> python client.py

awesome.Add(12,99) = 111

awesome.Cosine(1) = 0.540302

awesome.Sort(74,4,122,9,12) = [4, 9, 12, 74, 122]

Hello Python!

log id 1

 

Python CFFI 이용

 

다음 예제는 @sbinet 이 제공한 것입니다.

 

Python에는 Python2/Python3/pypy에서 변경 없이 이식 가능한 CFFI 라이브러리도 있습니다. 다음 예에서는 C-래퍼를 사용하여 내보낸 Go 유형을 정의합니다.

 

파일 client-cffi.py

 

from __future__ import print_function

import sys

from cffi import FFI

 

is_64b = sys.maxsize > 2**32

 

ffi = FFI()

if is_64b: ffi.cdef("typedef long GoInt;\n")

else: ffi.cdef("typedef int GoInt;\n")

 

ffi.cdef("""

typedef struct {

void* data;

GoInt len;

GoInt cap;

} GoSlice;

 

typedef struct {

const char *data;

GoInt len;

} GoString;

 

GoInt Add(GoInt a, GoInt b);

double Cosine(double v);

void Sort(GoSlice values);

GoInt Log(GoString str);

""")

 

lib = ffi.dlopen("./awesome.so")

 

print("awesome.Add(12,99) = %d" % lib.Add(12,99))

print("awesome.Cosine(1) = %f" % lib.Cosine(1))

 

data = ffi.new("GoInt[]", [74,4,122,9,12])

nums = ffi.new("GoSlice*", {'data':data, 'len':5, 'cap':5})

lib.Sort(nums[0])

print("awesome.Sort(74,4,122,9,12) = %s" % [

ffi.cast("GoInt*", nums.data)[i]

for i in range(nums.len)])

 

data = ffi.new("char[]", b"Hello Python!")

msg = ffi.new("GoString*", {'data':data, 'len':13})

print("log id %d" % lib.Log(msg[0]))

 

루비에서 호출

 

Ruby에서 Go 함수를 호출하는 것은 위와 비슷한 패턴을 따릅니다. 다음 스니펫과 같이 FFI gem을 사용하여 awesome.so 공유 오브젝트 파일에서 내보낸 Go 함수를 동적으로 로드하고 호출합니다.

 

파일 client.rb

 

require 'ffi'

 

# Module that represents shared lib

module Awesome

extend FFI::Library

 

ffi_lib './awesome.so'

 

# define class GoSlice to map to:

# C type struct { void *data; GoInt len; GoInt cap; }

class GoSlice < FFI::Struct

layout :data, :pointer,

:len, :long_long,

:cap, :long_long

end

 

# define class GoString to map:

# C type struct { const char *p; GoInt n; }

class GoString < FFI::Struct

layout :p, :pointer,

:len, :long_long

end

 

# foreign function definitions

attach_function :Add, [:long_long, :long_long], :long_long

attach_function :Cosine, [:double], :double

attach_function :Sort, [GoSlice.by_value], :void

attach_function :Log, [GoString.by_value], :int

end

 

# Call Add

print "awesome.Add(12, 99) = ", Awesome.Add(12, 99), "\n"

 

# Call Cosine

print "awesome.Cosine(1) = ", Awesome.Cosine(1), "\n"

 

# call Sort

nums = [92,101,3,44,7]

ptr = FFI::MemoryPointer.new :long_long, nums.size

ptr.write_array_of_long_long nums

slice = Awesome::GoSlice.new

slice[:data] = ptr

slice[:len] = nums.size

slice[:cap] = nums.size

Awesome.Sort(slice)

sorted = slice[:data].read_array_of_long_long nums.size

print "awesome.Sort(", nums, ") = ", sorted, "\n"

 

# Call Log

msg = "Hello Ruby!"

gostr = Awesome::GoString.new

gostr[:p] = FFI::MemoryPointer.from_string(msg)

gostr[:len] = msg.size

print "logid ", Awesome.Log(gostr), "\n"

 

Ruby에서는 FFI 공유 라이브러리에서 로드되는 기호를 선언하기 위해 모듈을 확장해야 합니다. 우리는 Ruby 클래스를 사용하여 GoSlice, GoString의 C 구조체를 매핑합니다. 코드를 실행하면 아래와 같이 Go 함수가 호출됩니다.

 

$> ruby client.rb

awesome.Add(12, 99) = 111

awesome.Cosine(1) = 0.5403023058681398

awesome.Sort([92, 101, 3, 44, 7]) = [3, 7, 44, 92, 101]

Hello Ruby!

 

Node에서 호출

 

Node의 경우 node-ffi(그리고 몇 가지 종속 패키지) 라는 외부 함수 라이브러리를 사용하여 다음 코드와 같이 awesome.so 공유 개체 파일에서 내보낸 Go 함수를 동적으로 로드하고 호출합니다.

 

파일 client.js

 

var ref = require("ref");

var ffi = require("ffi");

var Struct = require("ref-struct")

var ArrayType = require("ref-array")

 

var longlong = ref.types.longlong;

var LongArray = ArrayType(longlong);

 

// define object GoSlice to map to:

// C type struct { void *data; GoInt len; GoInt cap; }

var GoSlice = Struct({

data: LongArray,

len: "longlong",

cap: "longlong"

});

 

// define object GoString to map:

// C type struct { const char *p; GoInt n; }

var GoString = Struct({

p: "string",

n: "longlong"

});

 

// define foreign functions

var awesome = ffi.Library("./awesome.so", {

Add: ["longlong", ["longlong", "longlong"]],

Cosine: ["double", ["double"]],

Sort: ["void", [GoSlice]],

Log: ["longlong", [GoString]]

});

 

// call Add

console.log("awesome.Add(12, 99) = ", awesome.Add(12, 99));

 

// call Cosine

console.log("awesome.Cosine(1) = ", awesome.Cosine(1));

 

// call Sort

nums = LongArray([12,54,0,423,9]);

var slice = new GoSlice();

slice["data"] = nums;

slice["len"] = 5;

slice["cap"] = 5;

awesome.Sort(slice);

console.log("awesome.Sort([12,54,9,423,9] = ", nums.toArray());

 

// call Log

str = new GoString();

str["p"] = "Hello Node!";

str["n"] = 11;

awesome.Log(str);

 

노드는 ffi 오브젝트를 사용하여 공유 라이브러리에서 로드된 기호를 선언합니다. 또한 Node 구조체 개체를 사용하여 GoSlice, GoStringC 구조체에 매핑합니다. 코드를 실행하면 Go 함수가 호출됩니다.

 

awesome.Add(12, 99) = 111

awesome.Cosine(1) = 0.5403023058681398

awesome.Sort([12,54,9,423,9] = [ 0, 9, 12, 54, 423 ]

Hello Node!

 

자바에서 호출

 

Java에서 내보낸 Go 함수를 호출하기 위해 다음 코드에 표시된 대로 Java Native Access 라이브러리 또는 JNA를 사용합니다.

 

파일 client.java

 

import com.sun.jna.*;

import java.util.*;

import java.lang.Long;

 

public class Client {

public interface Awesome extends Library {

// GoSlice class maps to:

// C type struct { void *data; GoInt len; GoInt cap; }

public class GoSlice extends Structure {

public static class ByValue extends GoSlice implements Structure.ByValue {}

public Pointer data;

public long len;

public long cap;

protected List getFieldOrder(){

return Arrays.asList(new String[]{"data","len","cap"});

}

}

 

// GoString class maps to:

// C type struct { const char *p; GoInt n; }

public class GoString extends Structure {

public static class ByValue extends GoString implements Structure.ByValue {}

public String p;

public long n;

protected List getFieldOrder(){

return Arrays.asList(new String[]{"p","n"});

}

 

}

 

// Foreign functions

public long Add(long a, long b);

public double Cosine(double val);

public void Sort(GoSlice.ByValue vals);

public long Log(GoString.ByValue str);

}

 

static public void main(String argv[]) {

Awesome awesome = (Awesome) Native.loadLibrary(

"./awesome.so", Awesome.class);

 

System.out.printf("awesome.Add(12, 99) = %s\n", awesome.Add(12, 99));

System.out.printf("awesome.Cosine(1.0) = %s\n", awesome.Cosine(1.0));

 

// Call Sort

// First, prepare data array

long[] nums = new long[]{53,11,5,2,88};

Memory arr = new Memory(nums.length * Native.getNativeSize(Long.TYPE));

arr.write(0, nums, 0, nums.length);

// fill in the GoSlice class for type mapping

Awesome.GoSlice.ByValue slice = new Awesome.GoSlice.ByValue();

slice.data = arr;

slice.len = nums.length;

slice.cap = nums.length;

awesome.Sort(slice);

System.out.print("awesome.Sort(53,11,5,2,88) = [");

long[] sorted = slice.data.getLongArray(0,nums.length);

for(int i = 0; i < sorted.length; i++){

System.out.print(sorted[i] + " ");

}

System.out.println("]");

 

// Call Log

Awesome.GoString.ByValue str = new Awesome.GoString.ByValue();

str.p = "Hello Java!";

str.n = str.p.length();

System.out.printf("msgid %d\n", awesome.Log(str));

 

}

}

 

AwesomeJNA를 사용하기 위해 awesome.so 공유 라이브러리 파일에서 로드된 기호를 나타내는 Java 인터페이스를 정의합니다. 또한 GoSlice, GoString 클래스를 선언하고 해당 C 구조체 표현에 매핑합니다. 코드를 컴파일하고 실행하면 아래와 같이 내보낸 Go 함수가 호출됩니다.

 

$> javac -cp jna.jar Client.java

$> java -cp .:jna.jar Client

awesome.Add(12, 99) = 111

awesome.Cosine(1.0) = 0.5403023058681398

awesome.Sort(53,11,5,2,88) = [2 5 11 53 88 ]

Hello Java!

 

Lua에서 호출

 

이 예는 @titpetric 이 제공했습니다. (참고 LUA의 Calling Go 함수)

다음은 Lua에서 내보낸 Go 함수를 호출하는 방법을 보여줍니다. 이전과 마찬가지로 FFI 라이브러리를 사용하여 공유 오브젝트 파일을 동적으로 로드하고 내보낸 함수 기호에 바인딩합니다.

 

파일 client.lua

 

local ffi = require("ffi")

local awesome = ffi.load("./awesome.so")

 

ffi.cdef([[

typedef long long GoInt64;

typedef unsigned long long GoUint64;

typedef GoInt64 GoInt;

typedef GoUint64 GoUint;

typedef double GoFloat64;

 

typedef struct { const char *p; GoInt n; } GoString;

typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

 

extern GoInt Add(GoInt p0, GoInt p1);

extern GoFloat64 Cosine(GoFloat64 p0);

extern void Sort(GoSlice p0);

extern GoInt Log(GoString p0);

]]);

 

io.write( string.format("awesome.Add(12, 99) = %f\n", math.floor(tonumber(awesome.Add(12,99)))) )

io.write( string.format("awesome.Cosine(1) = %f\n", tonumber(awesome.Cosine(1))) )

 

local nums = ffi.new("long long[5]", {12,54,0,423,9})

local numsPointer = ffi.new("void *", nums);

local typeSlice = ffi.metatype("GoSlice", {})

local slice = typeSlice(numsPointer, 5, 5)

awesome.Sort(slice)

 

io.write("awesome.Sort([12,54,9,423,9] = ")

for i=0,4 do

if i > 0 then

io.write(", ")

end

io.write(tonumber(nums[i]))

end

io.write("\n");

 

local typeString = ffi.metatype("GoString", {})

local logString = typeString("Hello LUA!", 10)

awesome.Log(logString)

 

예제가 실행되면 다음이 생성됩니다.

 

$> luajit client.lua

awesome.Add(12, 99) = 111.000000

awesome.Cosine(1) = 0.540302

awesome.Sort([12,54,9,423,9] = 0, 9, 12, 54, 423

Hello LUA!

 

Julia에서 호출

 

다음 예는 @r9y9가 제공한 것입니다. Julia 언어에서 내보낸 Go 함수를 호출하는 방법을 보여줍니다. 여기에 설명된 대로 Julia에는 공유 라이브러리에서 내보낸 함수를 호출하는 기능이 있습니다.

 

파일 client.jl

 

struct GoSlice

arr::Ptr{Void}

len::Int64

cap::Int64

end

GoSlice(a::Vector, cap=10) = GoSlice(pointer(a), length(a), cap)

 

struct GoStr

p::Ptr{Cchar}

len::Int64

end

GoStr(s::String) = GoStr(pointer(s), length(s))

 

const libawesome = "awesome.so"

 

Add(x,y) = ccall((:Add, libawesome), Int,(Int,Int), x,y)

Cosine(x) = ccall((:Cosine, libawesome), Float64, (Float64,), x)

function Sort(vals)

ccall((:Sort, libawesome), Void, (GoSlice,), GoSlice(vals))

return vals # for convenience

end

Log(msg) = ccall((:Log, libawesome), Int, (GoStr,), GoStr(msg))

 

for ex in [:(Add(12, 9)),:(Cosine(1)), :(Sort([77,12,5,99,28,23]))]

println("awesome.$ex = $(eval(ex))")

end

Log("Hello from Julia!")

 

예제가 실행되면 다음이 생성됩니다.

 

> julia client.jl

awesome.Add(12, 9) = 21

awesome.Cosine(1) = 0.5403023058681398

awesome.Sort([77, 12, 5, 99, 28, 23]) = [5, 12, 23, 28, 77, 99]

Hello from Julia!

 

Dart에서 호출

 

다음 예는 @dpurfield가 제공한 것입니다. Dart 언어에서 내보낸 Go 함수를 호출하는 방법을 보여줍니다. Dart에는 여기에 설명된 다른 언어와 유사한 공유 라이브러리에서 내보낸 함수를 호출하는 기능이 있습니다.

 

파일 client.dart

 

import 'dart:convert';

import 'dart:ffi';

import 'dart:io';

 

class GoSlice extends Struct<GoSlice> {

Pointer<Int64> data;

 

@Int64()

int len;

 

@Int64()

int cap;

 

List<int> toList() {

List<int> units = [];

for (int i = 0; i < len; ++i) {

units.add(data.elementAt(i).load<int>());

}

return units;

}

 

static Pointer<GoSlice> fromList(List<int> units) {

final ptr = Pointer<Int64>.allocate(count: units.length);

for (int i =0; i < units.length; ++i) {

ptr.elementAt(i).store(units[i]);

}

final GoSlice slice = Pointer<GoSlice>.allocate().load();

slice.data = ptr;

slice.len = units.length;

slice.cap = units.length;

return slice.addressOf;

}

}

 

class GoString extends Struct<GoString> {

Pointer<Uint8> string;

 

@IntPtr()

int length;

 

String toString() {

List<int> units = [];

for (int i = 0; i < length; ++i) {

units.add(string.elementAt(i).load<int>());

}

return Utf8Decoder().convert(units);

}

 

static Pointer<GoString> fromString(String string) {

List<int> units = Utf8Encoder().convert(string);

final ptr = Pointer<Uint8>.allocate(count: units.length);

for (int i = 0; i < units.length; ++i) {

ptr.elementAt(i).store(units[i]);

}

final GoString str = Pointer<GoString>.allocate().load();

str.length = units.length;

str.string = ptr;

return str.addressOf;

}

}

 

typedef add_func = Int64 Function(Int64, Int64);

typedef Add = int Function(int, int);

typedef cosine_func = Double Function(Double);

typedef Cosine = double Function(double);

typedef log_func = Int64 Function(Pointer<GoString>);

typedef Log = int Function(Pointer<GoString>);

typedef sort_func = Void Function(Pointer<GoSlice>);

typedef Sort = void Function(Pointer<GoSlice>);

 

void main(List<String> args) {

 

final awesome = DynamicLibrary.open('awesome.so');

 

final Add add = awesome.lookup<NativeFunction<add_func>>('Add').asFunction();

stdout.writeln("awesome.Add(12, 99) = ${add(12, 99)}");

 

final Cosine cosine = awesome.lookup<NativeFunction<cosine_func>>('Cosine').asFunction();

stdout.writeln("awesome.Cosine(1) = ${cosine(1.0)}");

 

final Log log = awesome.lookup<NativeFunction<log_func>>('LogPtr').asFunction();

final Pointer<GoString> message = GoString.fromString("Hello, Dart!");

try {

log(message);

}

finally {

message.free();

}

 

final Sort sort = awesome.lookup<NativeFunction<sort_func>>('SortPtr').asFunction();

var nums = [12,54,0,423,9];

final Pointer<GoSlice> slice = GoSlice.fromList(nums);

try {

sort(slice);

stdout.writeln(slice.load<GoSlice>().toList());

} finally {

slice.free();

}

 

for (int i=0; i < 100000; i++) {

Pointer<GoString> m = GoString.fromString("Hello, Dart!");

Pointer<GoSlice> s = GoSlice.fromList(nums);

print("$m $s");

m.free();

s.free();

}

 

stdin.readByteSync();

}

 

C#에서 호출

 

C#에서 내보낸 Go 함수를 호출하기 위해 다음 코드처럼 DllImportAttribute 특성을 사용하여 awesome.so 공유 오브젝트 파일에서 내보낸 Go 함수를 동적으로 로드하고 호출합니다.

 

파일 client.cs

 

using System;

using System.Runtime.InteropServices;

 

class Awesome

{

const string libName = "awesome.so";

 

public struct GoSlice

{

public IntPtr data;

public long len, cap;

public GoSlice(IntPtr data, long len, long cap)

{

this.data = data;

this.len = len;

this.cap = cap;

}

}

public struct GoString

{

public string msg;

public long len;

public GoString(string msg, long len)

{

this.msg = msg;

this.len = len;

}

}

 

// Use DllImport to import the Awesome lib.

[DllImport(libName)]

public static extern int Add(long a, long b);

 

[DllImport(libName)]

public static extern double Cosine(double a);

 

[DllImport(libName)]

public static extern void Sort(GoSlice a);

 

[DllImport(libName, CharSet = CharSet.Unicode)]

public static extern void Log(GoString msg);

 

static void Main()

{

long add = Add(12, 99);

double cosine = Cosine(1);

 

long[] data = { 77, 12, 5, 99, 28, 23 };

IntPtr data_ptr = Marshal.AllocHGlobal(Buffer.ByteLength(data));

Marshal.Copy(data, 0, data_ptr, data.Length);

var nums = new GoSlice(data_ptr, data.Length, data.Length);

Sort(nums);

Marshal.Copy(nums.data, data, 0, data.Length);

 

string msg = "Hello from C#!";

GoString str = new GoString(msg, msg.Length);

 

Console.WriteLine("awesome.Add(12,99) = " + add);

Console.WriteLine("awesome.Cosine(1) = " + cosine);

Console.WriteLine("awesome.Sort(77,12,5,99,28,23): " + string.Join(", ", data));

Log(str);

}

}

 

예제가 실행되면 다음처럼 출력됩니다.

 

> dotnet run

awesome.Add(12,99) = 111

awesome.Cosine(1) = 0,5403023058681398

awesome.Sort(77,12,5,99,28,23): 5, 12, 23, 28, 77, 99

Hello from C#!

 

결론

 

Go 패키지를 C 형식의 공유 라이브러리로 컴파일함으로써 Go 프로그래머는 공유 오브젝트 파일의 동적 로딩 및 링크를 지원하는 최신 언어와 코드를 통합할 수 있습니다.

반응형

+ Recent posts