Giter Club home page Giter Club logo

go-cshared-examples's Introduction

Calling Go Functions from Other Languages using C Shared Libraries

This respository contains source examples for the article Calling Go Functions from Other Languages (medium.com). Using the -buildmode=c-shared build flag, the compiler outputs a standard shared object binary file (.so) exposing Go functions as a C-style APIs. This lets programmers create Go libraries that can be called from other languages including C, Python, Ruby, Node, and Java (see contributed example for Lua) as done in this repository.

The Go Code

First, let us write the Go code. Assume that we have written an awesome Go library that we want to make available to other languages. There are four requirements to follow before compiling the code into a shared library:

  • The package must be amain package. The compiler will build the package and all of its dependencies into a single shared object binary.
  • The source must import the pseudo-package “C”.
  • Use the //export comment to annotate functions you wish to make accessible to other languages.
  • An empty main function must be declared.

The following Go source exports four functions Add, Cosine, Sort, and Log. Admittedly, the awesome library is not that impressive. However, its diverse function signatures will help us explore type mapping implications.

File 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() {}

The package is compiled using the -buildmode=c-shared build flag to create the shared object binary:

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

Upon completion, the compiler outputs two files: awesome.h, a C header file and awesome.so, the shared object file, shown below:

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

Notice that the .so file is around 2 Mb, relatively large for such a small library. This is because the entire Go runtime machinery and dependent packages are crammed into a single shared object binary (similar to compiling a single static executable binary).

The header file

The header file defines C types mapped to Go compatible types using cgo semantics.

/* 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

The shared object file

The other file generated by the compiler is a 64-bit ELF shared object binary file. We can verify its information using the file command.

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

Using the nm and the grep commands, we can ensure our Go functions got exported in the shared object file.

$> 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

There are two ways to use the shared object library to call Go functions from C. First, we can statically bind the shared library at compilation, but dynamically link it at runtime. Or, have the Go function symbols be dynamically loaded and bound at runtime.

Dynamically linked

In this approach, we use the header file to statically reference types and functions exported in the shared object file. The code is simple and clean as shown below:

File 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);
}

Next we compile the C code, specifying the shared object library:

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

When the resulting binary is executed, it links to the awesome.so library, calling the functions that were exported from Go as the output shows below.

$> ./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

In this approach, the C code uses the dynamic link loader library (libdl.so) to dynamically load and bind exported symbols. It uses functions defined in dhfcn.h such as dlopen to open the library file, dlsym to look up a symbol, dlerror to retrieve errors, and dlclose to close the shared library file.

Because the binding and linking is done in your source code, this version is lengthier. However, it is doing the same thing as before, as highlighted in the following snippet (some print statements and error handling omitted).

File 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);
}

In the previous code, we define our own subset of Go compatible C types go_int, go_float, go_slice, and go_str. We use dlsym to load symbols Add, Cosine, Sort, and Log and assign them to their respective function pointers. Next, we compile the code linking it with the dl library (not the awesome.so) as follows:

$> gcc -o client client2.c -ldl

When the code is executed, the C binary loads and links to shared library awesome.so producing the following output:

$> ./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

In Python things get a little easier. We use can use the ctypes foreign function library to call Go functions from the the awesome.so shared library as shown in the following snippet (some print statements are omitted).

File 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))

Note the lib variable represents the loaded symbols from the shared object file. We also defined Python classes GoString and GoSlice to map to their respective C struct types. When the Python code is executed, it calls the Go functions in the shared object producing the following output:

$> 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 (contributed)

The following example was contributed by @sbinet (thank you!)

Python also has a portable CFFI library that works with Python2/Python3/pypy unchanged. The following example uses a C-wrapper to defined the exported Go types. This makes the python example less opaque and even easier to understand.

File 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]))

From Ruby

Calling Go functions from Ruby follows a similar pattern as above. We use the the FFI gem to dynamically load and call exported Go functions in the awesome.so shared object file as shown in the following snippet.

File 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"

In Ruby, we must extend the FFI module to declare the symbols being loaded from the shared library. We use Ruby classes GoSlice and GoString to map the respective C structs. When we run the code it calls the exported Go functions as shown below:

$> 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

For Node, we use a foreign function library called node-ffi (and a couple dependent packages) to dynamically load and call exported Go functions in the awesome.so shared object file as shown in the following snippet:

File 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);

Node uses the ffi object to declare the loaded symbols from the shared library . We also use Node struct objects GoSlice and GoString to map to their respective C structs. When we run the code it calls the exported Go functions as shown below:

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

To call the exported Go functions from Java, we use the Java Native Access library or JNA as shown in the following code snippet (with some statements omitted or abbreviated):

File 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));

    }
}

To use JNA, we define Java interface Awesome to represents the symbols loaded from the awesome.so shared library file. We also declare classes GoSlice and GoString to map to their respective C struct representations. When we compile and run the code, it calls the exported Go functions as shown below:

$> 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!

From Lua (contributed)

This example was contributed by @titpetric. See his insightful write up on Calling Go functions from LUA.

The forllowing shows how to invoke exported Go functions from Lua. As before, it uses an FFI library to dynamically load the shared object file and bind to the exported function symbols.

File 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)

When the example is executed, it produces the following:

$> 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!

From Julia (Contributed)

The following example was contributed by @r9y9. It shows how to invoke exported Go functions from the Julia language. As documented here, Julia has the capabilities to invoke exported functions from shared libraries similar to other languages discussed here.

File 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!")

When the example is executed, it produces the following:

> 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!

From Dart (Contributed)

The following example was contributed by @dpurfield. It shows how to invoke exported Go functions from the Dart language. As documented here, Dart has the capability to invoke exported functions from shared libraries similar to other languages discussed here.

File 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();
}

From C#

To call the exported Go functions from C# we use the DllImportAttribute attribute to dynamically load and call exported Go functions in the awesome.so shared object file as shown in the following snippet.

File 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);
    }
}

When the example is executed, it produces the following:

> 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#!

Conclusion

This repo shows how to create a Go library that can be used from C, Python, Ruby, Node, Java, Lua, Julia. By compiling Go packages into C-style shared libraries, Go programmers have a powerful way of integrating their code with any modern language that supports dynamic loading and linking of shared object files.

go-cshared-examples's People

Contributors

digital-512 avatar dpurfield avatar hellcoderz avatar nikhilgk avatar r9y9 avatar sbinet avatar synodriver avatar titpetric avatar vladimirvivien avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-cshared-examples's Issues

How to pass java string array into golang&java list to golang slice

Hello, need your help

I use []string in golang now, so how to call it in java?

package main

import (
	"C"
	"fmt"
	"github.com/xuri/excelize/v2"
	"math/rand"
	"strconv"
)

func main() {
	var tableHeaderEn []string
	tableHeaderEn = []string{"Item", "Name", "Age", "Gender", "Project", "Time"}
	testTimeExcel2("Book3.xlsx", "Sheet1", tableHeaderEn)

	//go build -buildmode=c-shared -o excel_stream.dll
	//go build -ldflags "-s -w" -buildmode=c-shared -o excel_stream.dll excel_stream.go
}

//export testTimeExcel2
func testTimeExcel2(excelFilePath string, iterationName string, tableHeaderEn []string) {
	fmt.Println("Start generating test time excel.")
	var xlsx *excelize.File
	var err error
	if xlsx, err = excelize.OpenFile(excelFilePath); err != nil {
		xlsx = excelize.NewFile()
	}

	streamWriter, err := xlsx.NewStreamWriter(iterationName) //"Sheet1" Ctrl+Alt+Y
	if err != nil {
		fmt.Println(err)
	}

	//表头样式
	styleIdEn, err := xlsx.NewStyle(&excelize.Style{Font: &excelize.Font{Color: "#777777"}})
	if err != nil {
		fmt.Println(err)
	}

	styleIdCn, err := xlsx.NewStyle(&excelize.Style{Font: &excelize.Font{Bold: true, Family: "Times New Roman"}})
	if err != nil {
		fmt.Println(err)
	}

	//表头名称第一行
	//var tableHeaderEn []string
	//tableHeaderEn = []string{"Item", "Name", "Age", "Gender", "Project", "Time"}
	var rowIdEn = 1
	for index, item := range tableHeaderEn {
		if err := streamWriter.SetRow(cellLocation(index, rowIdEn), []interface{}{excelize.Cell{StyleID: styleIdEn, Value: item}}); err != nil {
			fmt.Println(err)
		}
	}

	//隐藏第一行
	err = xlsx.SetRowVisible(iterationName, rowIdEn, false)
	if err != nil {
		fmt.Println(err)
	}

	//表头名称第二行
	var tableHeaderCn []string
	tableHeaderCn = []string{"序号", "姓名", "年龄", "性别", "工程", "时长"}
	var rowIdCn = 2
	for index, item := range tableHeaderCn {
		if err := streamWriter.SetRow(cellLocation(index, rowIdCn), []interface{}{excelize.Cell{StyleID: styleIdCn, Value: item}}); err != nil {
			fmt.Println(err)
		}
	}

	//第三行开始
	for rowID := 3; rowID <= 10; rowID++ {
		row := make([]interface{}, 6)
		for colID := 0; colID < 6; colID++ {
			row[colID] = rand.Intn(640000)
		}
		cell, _ := excelize.CoordinatesToCellName(1, rowID)
		if err := streamWriter.SetRow(cell, row); err != nil {
			fmt.Println(err)
		}
	}

	index := xlsx.GetSheetIndex(iterationName)
	xlsx.SetActiveSheet(index)

	if err := streamWriter.Flush(); err != nil {
		fmt.Println(err)
	}

	fmt.Println("End generating test time excel.")
	if err := xlsx.SaveAs(excelFilePath); err != nil {
		fmt.Println(err)
	}
	//return //xlsx.SaveAs(excelFilePath)
}

// cellLocation get cell location with given column and row
func cellLocation(c int, r int) (cellLocation string) {
	sc := 'A'
	if c < 26 {
		cellLocation = string(int(sc)+c) + strconv.Itoa(r)
	} else {
		cellLocation = "A" + string(int(sc)+c-26) + strconv.Itoa(r)
	}
	return
}

excel_stream.h

/* Code generated by cmd/cgo; DO NOT EDIT. */

/* package strong-excelize/hellp */


#line 1 "cgo-builtin-export-prolog"

#include <stddef.h> /* for ptrdiff_t below */

#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif

#endif

/* Start of preamble from import "C" comments.  */




/* End of preamble from import "C" comments.  */


/* Start of boilerplate cgo prologue.  */
#line 1 "cgo-gcc-export-header-prolog"

#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H

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];

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
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 __declspec(dllexport) void testTimeExcel2(GoString excelFilePath, GoString iterationName, GoSlice tableHeaderEn);

#ifdef __cplusplus
}
#endif

Thanks again

Rust example

Hi,

It would be helpful if there was a Rust example.

I tried though wasn't able to get the linking flags to compile.

Cargo.toml

[package]
name = "client_rust"
version = "0.1.0"
authors = ["example"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libc = "0.2"

src/main.rs

use libc::c_long;


#[link(name = "awesome", kind="dylib")]
extern {
    fn Add(num1: c_long, num2: c_long) -> ();
}

fn main() {
    unsafe { Add(5,5); }
    println!("Ran the unsafe code.");
}

how execute csharp method from go c-shared library?

I tried use the a go library on csharp and use callbacks, but the application entire crash by the library with the error:
unexpected fault address 0xffffffffffffffff fatal error: fault [signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x627ca9ab]

go code:

package main

import "C"
import "fmt"

type externFunc func(int)

//export Connect
func Connect(fn externFunc) {
    fmt.Println("fn ",fn)

    for i:= 0; i<3;i++{
       fn(10)
    }

} 

How is the way to execute callbacks with c-shared?

[Question] C# string array to GO slice

Hi,

I've successfully used the C# example of:

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;
    }
}

[DllImport(libName)]
public static extern void Sort(GoSlice a);

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);

To be able to pass in a C# int array to a GO []int.

But how would I go on about passing a C# string array to a GO []string?

Let's say we have the following function in the GO code:

//export StringArray
func StringArray(strings []string) {
	for _, element := range strings {
		fmt.Println(element)
	}
}

How would I call this, similar as to the []int call above?

Any input is appreciated!

how to use `GoInterface` struct in CGO?

like the code below:

t.go
//export testFunc
func testFunc() (a interface{}) {
 a = 100
 return
}
t.h
typedef struct { void *t; void *v; } GoInterface;
// ...
extern GoInterface testFunc();

we export a function through CGO like extern GoInterface testFunc();, how to call this from C or Lua or others? how to initialization and using a GoInterface in C, or in the Luajit FFI context?

loading go shared library from python on alpine report OSError: Error relocating ./cc.so: : initial-exec TLS resolves to dynamic definition in ./cc.so

/usr/src/app # go build -o cc.so -buildmode=c-shared main.go

/usr/src/app # readelf -d cc.so
Dynamic section at offset 0x10cd10 contains 22 entries: Tag Type Name/Value 0x0000000000000001 (NEEDED) Shared library: [libc.musl-x86_64.so.1] 0x0000000000000010 (SYMBOLIC) 0x0 0x000000000000000c (INIT) 0x42000 0x000000000000000d (FINI) 0x92ed9 0x0000000000000019 (INIT_ARRAY) 0xa2078 0x000000000000001b (INIT_ARRAYSZ) 8 (bytes) 0x000000006ffffef5 (GNU_HASH) 0x270 0x0000000000000005 (STRTAB) 0xa50 0x0000000000000006 (SYMTAB) 0x378 0x000000000000000a (STRSZ) 1026 (bytes) 0x000000000000000b (SYMENT) 24 (bytes) 0x0000000000000003 (PLTGOT) 0x10deb0 0x0000000000000002 (PLTRELSZ) 720 (bytes) 0x0000000000000014 (PLTREL) RELA 0x0000000000000017 (JMPREL) 0x41a00 0x0000000000000007 (RELA) 0xe58 0x0000000000000008 (RELASZ) 265128 (bytes) 0x0000000000000009 (RELAENT) 24 (bytes) 0x000000000000001e (FLAGS) SYMBOLIC BIND_NOW STATIC_TLS 0x000000006ffffffb (FLAGS_1) Flags: NOW NODELETE 0x000000006ffffff9 (RELACOUNT) 11040 0x0000000000000000 (NULL) 0x0

/usr/src/app # python test.py
Traceback (most recent call last):

File "test.py", line 2, in

lib = ctypes.cdll.LoadLibrary('./cc.so')

File "/usr/lib/python2.7/ctypes/init.py", line 444, in LoadLibrary

return self._dlltype(name)

File "/usr/lib/python2.7/ctypes/init.py", line 366, in init

self._handle = _dlopen(self._name, mode)

OSError: Error relocating ./cc.so: : initial-exec TLS resolves to dynamic definition in ./cc.so

Dart Example gives error with Dart 2.9.0-14.1.beta on MacOS

@dpurfield Thank you for giving Dart Example for FFI. But when I execute it, it gives the following errors.

Would really appreciate if you can help.

~ dart --version
Dart VM version: 2.9.0-14.1.beta (beta) (Tue Jun 9 10:52:57 2020 +0200) on "macos_x64"
$ dart client.dart 
client.dart:5:23: Error: Expected 0 type arguments.
class GoSlice extends Struct<GoSlice> {
                      ^
client.dart:35:24: Error: Expected 0 type arguments.
class GoString extends Struct<GoString> {
                       ^
client.dart:22:35: Error: Type argument 'GoSlice' doesn't conform to the bound 'NativeType' of the type variable 'T' on 'Pointer' in the return type.
 - 'GoSlice' is from 'client.dart'.
 - 'NativeType' is from 'dart:ffi'.
Try changing type arguments so that they conform to the bounds.
  static Pointer<GoSlice> fromList(List<int> units) {
                                  ^
client.dart:49:38: Error: Type argument 'GoString' doesn't conform to the bound 'NativeType' of the type variable 'T' on 'Pointer' in the return type.
 - 'GoString' is from 'client.dart'.
 - 'NativeType' is from 'dart:ffi'.
Try changing type arguments so that they conform to the bounds.
  static Pointer<GoString> fromString(String string) {
                                     ^
client.dart:17:35: Error: The method 'load' isn't defined for the class 'Pointer<Int64>'.
 - 'Pointer' is from 'dart:ffi'.
 - 'Int64' is from 'dart:ffi'.
Try correcting the name to the name of an existing method, or defining a method named 'load'.
      units.add(data.elementAt(i).load<int>());
                                  ^^^^
client.dart:23:32: Error: Method not found: 'Pointer.allocate'.
    final ptr = Pointer<Int64>.allocate(count: units.length);
                               ^^^^^^^^
client.dart:27:44: Error: Method not found: 'Pointer.allocate'.
    final GoSlice slice = Pointer<GoSlice>.allocate().load();
                                           ^^^^^^^^
client.dart:31:18: Error: The getter 'addressOf' isn't defined for the class 'GoSlice'.
 - 'GoSlice' is from 'client.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'addressOf'.
    return slice.addressOf;
                 ^^^^^^^^^
client.dart:44:37: Error: The method 'load' isn't defined for the class 'Pointer<Uint8>'.
 - 'Pointer' is from 'dart:ffi'.
 - 'Uint8' is from 'dart:ffi'.
Try correcting the name to the name of an existing method, or defining a method named 'load'.
      units.add(string.elementAt(i).load<int>());
                                    ^^^^
client.dart:51:32: Error: Method not found: 'Pointer.allocate'.
    final ptr = Pointer<Uint8>.allocate(count: units.length);

Thanks,
Satinder

Any examples of libraries using this approach?

Thanks @vladimirvivien for your article and Github repository.

I was wondering if you know of any (popular) libraries that use this approach to allow that library to be used from many other languages?

When asking about this in the Go community, I'm getting a lot of fear and doubt that I shouldn't use c-shared libaries for that purpose. For example that the Go would expect to own the process it is running in, and any such attempt would be "fragile". What is your view on that?

[Question] - Pass struct/slice of structs from C# <---> Go

Hello once again @Digital-512

As mentioned in #27, I'm now seeking assistance for the task of passing a struct and also a slice of structs, from C# to a CGO exported function, and returning a struct and also a slice of structs, from a CGO exported function to the calling C# code.

Consider the following example struct in Go:

type Person struct {
	Name             string
	Hobbies          []string
	FavouriteNumbers []int
	Age              int
	Single           bool
}

Which is then called as a single struct or a slice of structs:

person := Person{
	Name:             "John",
	Hobbies:          []string{"Golf", "Hiking", "Outdoors "},
	FavouriteNumbers: []int{1, 2, 3},
	Age:              21,
	Single:           false,
}

people := []Person{
	{"John", []string{"Golf", "Hiking", "Outdoors "}, []int{1, 2, 3}, 21, false},
	{"Doe", []string{"Cycling"}, []int{4}, 32, false},
	// etc...
}

Building on top of your other provided snippets, what would be a suitable way of doing this?
How would I go on about passing both a single struct but also a slice of structs from C# to a CGO exported Go function, and also return them? Such as these examples:

type Person struct {
	Name             string
	Hobbies          []string
	FavouriteNumbers []int
	Age              int
	Single           bool
}

//export Struct
func Struct(person StructFromCSharpToGo) structFromGoToCSharp {

	// Use struct
	// return structToCSharp
}

//export StructSlice
func StructSlice(people StructSliceFromCSharpToGo) structSliceFromGoToCSharp  {

	// Use struct slice
	// return struct slice
}

func main() {}

Just get back if you need me to clearify anything, as I do see that I can get quite confusing in my examples and attempts to explain, because of my lack of knowledge in the topics.

[Question] C# byte[] <---> GO []byte (return to C# and input to GO)

@Digital-512 I'd once again like to thank you for your thorough answer on my other question #26. Not only did you provide excellent examples, but you also complemented them with explanations so that I actually managed to learn something at the same time.

Now I wonder if I'd be too bold, to once again ask for your input.

I managed to use the examples in this repository, to provide int inputs from C# to the exported Go Add() function, and then return an int from that exported Go function to the C# code.

However, I need to be able to input a C# byte[] to an exported GO function expecting a []byte input. I also need to return a []byte from that exported Go function to a C# byte[].

Consider the below examples.

Go exported function (pseduo):

//export ReturnBytes
func InputAndReturnBytes(byteSlice []byte) []byte {
	// Process the byteSlice []byte input
	// return processedByteSlice
}

From C#, how would I input a byte[] to the byteSlice []byte argument? How would I assign the returned []byte/GoSlice to a byte[] in C#?

Looking at the generated header file for such an exported function:
extern __declspec(dllexport) GoSlice InputAndReturnBytes(GoSlice byteSlice);

One can see that it expects a GoSlice for the byteSlice input parameter, and returns a GoSlice.

I've tried in various ways, e.g.:

  • To input a C# byte[] using the GoSlice struct which is defined earlier (also in the examples of this repository).
  • Assign the returned GoSlice from the exported Go function to the C# GoSlice struct.

But unfortunately both to no avail.

I struggle on how to work with the byte array/slice between the two languages/interfaces.

I can see that in Go:

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

And in the CGO generated header file, that typedef unsigned char GoUint8; is actually defined.

Perhaps I need to create a GoByte/GoUint8 struct of sorts in C#, and then put it in the GoSlice? I actually have no idea.

As you can see, I have no idea what I'm doing here. 🙄
Crossing my fingers you'll have some time over to shed some light. 🤞

Go type not supported in export: lingua.Language

I would like to call lingua-go functions from C++ code

I tried to generate h file and so file for this code ( following the indications found here: https://github.com/vladimirvivien/go-cshared-examples)

basic.go :

package main

import "C"

import (
        "github.com/pemistahl/lingua-go"
)

var lan lingua.Language

//export Langdetectfunct
func Langdetectfunct(text string) lingua.Language {

    detector := lingua.NewLanguageDetectorBuilder().
        FromAllLanguages().
        Build()

    if language, exists := detector.DetectLanguageOf(text); exists {
        lan = language
    }
    lan = lingua.English

    return lan

}

func main() {}

Doing:

raphy@raohy:~/go-lang-detect$ go build -o basic.so -buildmode=c-shared basic.go

I get :

raphy@raohy:~/go-lang-detect$ go build -o basic.so -buildmode=c-shared basic.go
# command-line-arguments
./basic.go:14:35: Go type not supported in export: lingua.Language

EINVAL error from go runtime in dynamically linked case ?

Thanks for the examples.
I am playing a little bit with cgo at the moment and realized from your C examples that in the dynamically linked case (client1) the errno value is already set to EINVAL before even running the client code.
Whereas in the dynamically loaded case (client2), no error is set.

I have just run the two cases, and added printf("%s", strerror(errno)) at the very beginning of the main functions.

Am I missing something or is it that the initialization of the Go runtime may have left an error un-handled in the first case ?

-buildmode=c-shared not supported on windows/amd64

Im using go 1.9.3 and just tried to do this with this simple file :

package main

import "C"
import (
	"fmt"
)

//export add
func add(a, b int) int {
	var sum int = a + b
	fmt.Println("DONE")
	return sum
}

func main() {}

But kept getting the error above. Is there any way to fix this? Am I using the wrong command?

Image not found

⋊> ~/G/go-cshared-examples on master ⨯ go build -o awesome.so -buildmode=c-shared awesome.go                                                  14:23:25

⋊> ~/G/go-cshared-examples on master ⨯ ll                                                                                                     14:23:35
total 3984
-rw-r--r--   1 marcelaraujo  staff   2.4K Mar  8 14:16 Client.java
-rw-r--r--   1 marcelaraujo  staff   1.0K Mar  8 14:16 LICENSE
-rw-r--r--   1 marcelaraujo  staff    18K Mar  8 14:16 README.md
-rw-r--r--   1 marcelaraujo  staff   419B Mar  8 14:16 awesome.go
-rw-r--r--   1 marcelaraujo  staff   1.4K Mar  8 14:23 awesome.h
-rw-r--r--   1 marcelaraujo  staff   1.9M Mar  8 14:23 awesome.so

⋊> ~/G/go-cshared-examples on master ⨯ node client.js                                                                                         14:20:44
/Users/marcelaraujo/GoProjects/go-cshared-examples/node_modules/ffi/lib/dynamic_library.js:74
    throw new Error('Dynamic Linking Error: ' + err)
    ^

Error: Dynamic Linking Error: dlopen(./awesome.so.dylib, 2): image not found
    at new DynamicLibrary (/Users/marcelaraujo/GoProjects/go-cshared-examples/node_modules/ffi/lib/dynamic_library.js:74:11)
    at Object.Library (/Users/marcelaraujo/GoProjects/go-cshared-examples/node_modules/ffi/lib/library.js:45:12)
    at Object.<anonymous> (/Users/marcelaraujo/GoProjects/go-cshared-examples/client.js:25:19)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:425:7)

header file is not generated

main.go:
package main

//export add
func add(a, b int) int {
return a + b
}

func main() {}

command:
go build -o libgo.so -buildmode=c-shared main.go

there is no error but only libgo.so generated.

Error: Dynamic Linking Error: Win32 error 126 in client.js

I'm on Windows 10 x64 bit and I am getting this problem. I have the code copied exactly the same way except for the first four requirements because the old ones seemed to be deprecated

Error: Dynamic Linking Error: Win32 error 126

Coming from

var awesome = ffi.Library("./awesome.so", {
  Add: ["longlong", ["longlong", "longlong"]],
  Cosine: ["double", ["double"]],
  Sort: ["void", [GoSlice]],
  Log: ["longlong", [GoString]]
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.