Calling Go Functions from Other Languages

Vladimir Vivien
Feb 24, 2017 · 8 min read

The Go Code

package mainimport "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() {}// See awesome.go GitHub
go build -o awesome.so -buildmode=c-shared awesome.go
-rw-rw-r —    1362 Feb 11 07:59 awesome.h
-rw-rw-r — 1997880 Feb 11 07:59 awesome.so

The header file

/* Created by “go tool cgo” — DO NOT EDIT. */
...
typedef long long GoInt64;
typedef GoInt64 GoInt;
...
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);
...

The shared object file

$> file awesome.so
awesome.so: ELF 64-bit LSB shared object, x86–64, version 1 (SYSV), dynamically linked, BuildID[sha1]=1fcf29a2779a335371f17219fffbdc47b2ed378a, not stripped
$> 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

From C

Dynamically linked

#include <stdio.h>
#include "awesome.h"
int main() {
GoInt a = 12;
GoInt b = 99;
printf("awesome.Add(12,99) = %d\n", Add(a, b));
printf("awesome.Cosine(1) = %f\n", (float)(Cosine(1.0)));
GoInt data[6] = {77, 12, 5, 99, 28, 23};
GoSlice nums = {data, 6, 6};
Sort(nums);
for (int i = 0; i < 6; i++){
printf("%d,", ((GoInt *)nums.data)[i]);
}
GoString msg = {"Hello from C!", 13};
Log(msg);
}
// See client1.c on GitHub
$> gcc -o client client1.c ./awesome.so
$> ./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!

Dynamically Loaded

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
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;
handle = dlopen ("./awesome.so", RTLD_LAZY);
if (!handle) {
fputs (dlerror(), stderr);
exit(1);
}
go_int (*add)(go_int, go_int) = dlsym(handle, "Add");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
go_int sum = (*add)(12, 99);
printf("awesome.Add(12, 99) = %d\n", sum);

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

void (*sort)(go_slice) = dlsym(handle, "Sort");
go_int data[5] = {44,23,7,66,2};
go_slice nums = {data, 5, 5};
sort(nums);

go_int (*log)(go_str) = dlsym(handle, "Log");
go_str msg = {"Hello from C!", 13};
log(msg);

dlclose(handle);
}
// See client2.c on GitHub
$> gcc -o client client2.c -ldl
$> ./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!

From Python

from ctypes import *lib = cdll.LoadLibrary("./awesome.so")lib.Add.argtypes = [c_longlong, c_longlong]
print "awesome.Add(12,99) = %d" % lib.Add(12,99)
lib.Cosine.argtypes = [c_double]
lib.Cosine.restype = c_double
cos = lib.Cosine(1)
print "awesome.Cosine(1) = %f" % cos
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)
lib.Sort.argtypes = [GoSlice]
lib.Sort.restype = None
lib.Sort(nums)
class GoString(Structure):
_fields_ = [("p", c_char_p), ("n", c_longlong)]
lib.Log.argtypes = [GoString]
msg = GoString(b"Hello Python!", 13)
lib.Log(msg)
# See client.py on GitHub
$> 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!

From Ruby

require 'ffi'module Awesome
extend FFI::Library
ffi_lib './awesome.so' class GoSlice < FFI::Struct
layout :data, :pointer,
:len, :long_long,
:cap, :long_long
end
class GoString < FFI::Struct
layout :p, :pointer,
:len, :long_long
end
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

print "awesome.Add(12, 99) = ", Awesome.Add(12, 99), "\n"
print "awesome.Cosine(1) = ", Awesome.Cosine(1), "\n"
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)
msg = "Hello Ruby!"
gostr = Awesome::GoString.new
gostr[:p] = FFI::MemoryPointer.from_string(msg)
gostr[:len] = msg.size
Awesome.Log(gostr)
# See client.rb on GitHub
$> 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!

From Node

var ref = require("ref");
var ffi = require("ffi");
var Struct = require("ref-struct")
var ArrayType = require("ref-array")
var LongArray = ArrayType(ref.types.longlong);
var GoSlice = Struct({
data: LongArray,
len: "longlong",
cap: "longlong"
});
var GoString = Struct({
p: "string",
n: "longlong"
});
var awesome = ffi.Library("./awesome.so", {
Add: ["longlong", ["longlong", "longlong"]],
Cosine: ["double", ["double"]],
Sort: ["void", [GoSlice]],
Log: ["longlong", [GoString]]
});
console.log("awesome.Add(12, 99) = ", awesome.Add(12, 99));
console.log("awesome.Cosine(1) = ", awesome.Cosine(1));
nums = LongArray([12,54,0,423,9]);
var slice = new GoSlice();
slice["data"] = nums;
slice["len"] = 5;
slice["cap"] = 5;
awesome.Sort(slice);
str = new GoString();
str["p"] = "Hello Node!";
str["n"] = 11;
awesome.Log(str);
// See client.js on GitHub
awesome.Add(12, 99) =  111
awesome.Cosine(1) = 0.5403023058681398
awesome.Sort([12,54,9,423,9] = [ 0, 9, 12, 54, 423 ]
Hello Node!

From Java

import com.sun.jna.*;public class Client {
public interface Awesome extends Library {
public class GoSlice extends Structure {
...
public Pointer data;
public long len;
public long cap;
}

public class GoString extends Structure {
...
public String p;
public long n;
}
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));
System.out.printf(... awesome.Cosine(1.0));
long[] nums = new long[]{53,11,5,2,88};
Memory arr = new Memory(... Native.getNativeSize(Long.TYPE));
Awesome.GoSlice.ByValue slice = new Awesome.GoSlice.ByValue();
slice.data = arr;
slice.len = nums.length;
slice.cap = nums.length;
awesome.Sort(slice);
Awesome.GoString.ByValue str = new Awesome.GoString.ByValue();
str.p = "Hello Java!";
str.n = str.p.length();
System.out.printf(... awesome.Log(str));
}
}
// See Client.java on GitHub
$> 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!

Conclusion

Learning the Go Programming Language

Short and insightful posts for newcomers learning the Go programming language

Vladimir Vivien

Written by

Software Eng • Go Programming • Kubernetes • Author http://golang.fyi

Learning the Go Programming Language

Short and insightful posts for newcomers learning the Go programming language