[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-9508":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":19,"hasPages":19,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":35,"readmeContent":36,"aiSummary":37,"trendingCount":16,"starSnapshotCount":16,"syncStatus":38,"lastSyncTime":39,"discoverSource":40},9508,"go-cshared-examples","vladimirvivien\u002Fgo-cshared-examples","vladimirvivien","Calling Go Functions from Other Languages using C Shared Libraries","",null,"Dart",952,113,29,8,0,50.17,"MIT License",false,"master",[22,23,24,25,26,27,28,29,30,31,32,33,34],"dartlang","dynamic-library","ffi","go","golang","java","jna","julia","lua","nodejs","python","ruby","shared-libraries","2026-06-12 04:00:45","# Calling Go Functions from Other Languages using C Shared Libraries\n\nThis respository contains source examples for the article [*Calling Go Functions from Other Languages*](https:\u002F\u002Fmedium.com\u002Flearning-the-go-programming-language\u002Fcalling-go-functions-from-other-languages-4c7d8bcc69bf#.n73as5d6d) (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.\n\n## The Go Code\nFirst, 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: \n\n* The package must be amain  package. The compiler will build the package and all of its dependencies into a single shared object binary.\n* The source must import the pseudo-package “C”.\n* Use the \u002F\u002Fexport comment to annotate functions you wish to make accessible to other languages.\n* An empty main function must be declared.\n\nThe 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.\n\nFile [awesome.go](.\u002Fawesome.go)\n```go\npackage main\n\nimport \"C\"\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"sort\"\n\t\"sync\"\n)\n\nvar count int\nvar mtx sync.Mutex\n\n\u002F\u002Fexport Add\nfunc Add(a, b int) int {\n\treturn a + b\n}\n\n\u002F\u002Fexport Cosine\nfunc Cosine(x float64) float64 {\n\treturn math.Cos(x)\n}\n\n\u002F\u002Fexport Sort\nfunc Sort(vals []int) {\n\tsort.Ints(vals)\n}\n\n\u002F\u002Fexport Log\nfunc Log(msg string) int {\n\tmtx.Lock()\n\tdefer mtx.Unlock()\n\tfmt.Println(msg)\n\tcount++\n\treturn count\n}\n\nfunc main() {}\n```\n\nThe package is compiled using the `-buildmode=c-shared` build flag to create the shared object binary:\n```shell\ngo build -o awesome.so -buildmode=c-shared awesome.go\n```\nUpon completion, the compiler outputs two files: `awesome.h`, a C header file and `awesome.so`, the shared object file, shown below:\n```shell\n-rw-rw-r —    1362 Feb 11 07:59 awesome.h\n-rw-rw-r — 1997880 Feb 11 07:59 awesome.so\n```\nNotice 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).\n\n### The header file\nThe header file defines C types mapped to Go compatible types using cgo semantics. \n```c\n\u002F* Created by “go tool cgo” — DO NOT EDIT. *\u002F\n...\ntypedef signed char GoInt8;\ntypedef unsigned char GoUint8;\ntypedef short GoInt16;\ntypedef unsigned short GoUint16;\ntypedef int GoInt32;\ntypedef unsigned int GoUint32;\ntypedef long long GoInt64;\ntypedef unsigned long long GoUint64;\ntypedef GoInt64 GoInt;\ntypedef GoUint64 GoUint;\ntypedef __SIZE_TYPE__ GoUintptr;\ntypedef float GoFloat32;\ntypedef double GoFloat64;\ntypedef float _Complex GoComplex64;\ntypedef double _Complex GoComplex128;\n\n\u002F*\n  static assertion to make sure the file is being used on architecture\n  at least with matching size of GoInt.\n*\u002F\ntypedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64\u002F8 ? 1:-1];\n\ntypedef struct { const char *p; GoInt n; } GoString;\ntypedef void *GoMap;\ntypedef void *GoChan;\ntypedef struct { void *t; void *v; } GoInterface;\ntypedef struct { void *data; GoInt len; GoInt cap; } GoSlice;\n\n#endif\n\n\u002F* End of boilerplate cgo prologue.  *\u002F\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\nextern GoInt Add(GoInt p0, GoInt p1);\n\nextern GoFloat64 Cosine(GoFloat64 p0);\n\nextern void Sort(GoSlice p0);\n\nextern GoInt Log(GoString p0);\n\n#ifdef __cplusplus\n}\n#endif\n```\n### The shared object file\nThe other file generated by the compiler is a 64-bit ELF shared object binary file. We can verify its information using the `file` command. \n```shell\n$> file awesome.so\nawesome.so: ELF 64-bit LSB shared object, x86–64, version 1 (SYSV), dynamically linked, BuildID[sha1]=1fcf29a2779a335371f17219fffbdc47b2ed378a, not stripped\n```\nUsing the `nm` and the `grep` commands, we can ensure our Go functions got exported in the shared object file.\n```shell\n$> nm awesome.so | grep -e \"T Add\" -e \"T Cosine\" -e \"T Sort\" -e \"T Log\"\n00000000000d0db0 T Add\n00000000000d0e30 T Cosine\n00000000000d0f30 T Log\n00000000000d0eb0 T Sort\n```\n## From C\nThere 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.\n### Dynamically linked\nIn 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:\n\nFile [client1.c](.\u002Fclient1.c)\n```c\n#include \u003Cstdio.h>\n#include \"awesome.h\"\n\nint main() {\n    printf(\"Using awesome lib from C:\\n\");\n   \n    \u002F\u002FCall Add() - passing integer params, interger result\n    GoInt a = 12;\n    GoInt b = 99;\n    printf(\"awesome.Add(12,99) = %d\\n\", Add(a, b)); \n\n    \u002F\u002FCall Cosine() - passing float param, float returned\n    printf(\"awesome.Cosine(1) = %f\\n\", (float)(Cosine(1.0)));\n    \n    \u002F\u002FCall Sort() - passing an array pointer\n    GoInt data[6] = {77, 12, 5, 99, 28, 23};\n    GoSlice nums = {data, 6, 6};\n    Sort(nums);\n    printf(\"awesome.Sort(77,12,5,99,28,23): \");\n    for (int i = 0; i \u003C 6; i++){\n        printf(\"%d,\", ((GoInt *)nums.data)[i]);\n    }\n    printf(\"\\n\");\n\n    \u002F\u002FCall Log() - passing string value\n    GoString msg = {\"Hello from C!\", 13};\n    Log(msg);\n}\n```\nNext we compile the C code, specifying the shared object library:\n```shell\n$> gcc -o client client1.c .\u002Fawesome.so\n```\nWhen 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.\n```shell\n$> .\u002Fclient\nawesome.Add(12,99) = 111\nawesome.Cosine(1) = 0.540302\nawesome.Sort(77,12,5,99,28,23): 5,12,23,28,77,99,\nHello from C!\n```\n### Dynamically Loaded\nIn 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.\n\nBecause 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).\n\nFile [client2.c](.\u002Fclient2.c)\n```c\n#include \u003Cstdlib.h>\n#include \u003Cstdio.h>\n#include \u003Cdlfcn.h>\n\n\u002F\u002F define types needed\ntypedef long long go_int;\ntypedef double go_float64;\ntypedef struct{void *arr; go_int len; go_int cap;} go_slice;\ntypedef struct{const char *p; go_int len;} go_str;\n\nint main(int argc, char **argv) {\n    void *handle;\n    char *error;\n\n    \u002F\u002F use dlopen to load shared object\n    handle = dlopen (\".\u002Fawesome.so\", RTLD_LAZY);\n    if (!handle) {\n        fputs (dlerror(), stderr);\n        exit(1);\n    }\n    \n    \u002F\u002F resolve Add symbol and assign to fn ptr\n    go_int (*add)(go_int, go_int)  = dlsym(handle, \"Add\");\n    if ((error = dlerror()) != NULL)  {\n        fputs(error, stderr);\n        exit(1);\n    }\n    \u002F\u002F call Add()\n    go_int sum = (*add)(12, 99); \n    printf(\"awesome.Add(12, 99) = %d\\n\", sum);\n\n    \u002F\u002F resolve Cosine symbol\n    go_float64 (*cosine)(go_float64) = dlsym(handle, \"Cosine\");\n    if ((error = dlerror()) != NULL)  {\n        fputs(error, stderr);\n        exit(1);\n    }\n    \u002F\u002F Call Cosine\n    go_float64 cos = (*cosine)(1.0);\n    printf(\"awesome.Cosine(1) = %f\\n\", cos);\n\n    \u002F\u002F resolve Sort symbol\n    void (*sort)(go_slice) = dlsym(handle, \"Sort\");\n    if ((error = dlerror()) != NULL)  {\n        fputs(error, stderr);\n        exit(1);\n    }\n    \u002F\u002F call Sort\n    go_int data[5] = {44,23,7,66,2};\n    go_slice nums = {data, 5, 5};\n    sort(nums);\n    printf(\"awesome.Sort(44,23,7,66,2): \");\n    for (int i = 0; i \u003C 5; i++){\n        printf(\"%d,\", ((go_int *)data)[i]);\n    }\n    printf(\"\\n\");\n\n    \u002F\u002F resolve Log symbol\n    go_int (*log)(go_str) = dlsym(handle, \"Log\");\n    if ((error = dlerror()) != NULL)  {\n        fputs(error, stderr);\n        exit(1);\n    }\n    \u002F\u002F call Log\n    go_str msg = {\"Hello from C!\", 13};\n    log(msg);\n    \n    \u002F\u002F close file handle when done\n    dlclose(handle);\n}\n```\nIn 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:\n```shell\n$> gcc -o client client2.c -ldl\n```\nWhen the code is executed, the C binary loads and links to shared library awesome.so producing the following output:\n```shell\n$> .\u002Fclient\nawesome.Add(12, 99) = 111\nawesome.Cosine(1) = 0.540302\nawesome.Sort(44,23,7,66,2): 2,7,23,44,66,\nHello from C!\n```\n## From Python\nIn Python things get a little easier. We use can use the `ctypes` [foreign function library](https:\u002F\u002Fdocs.python.org\u002F3\u002Flibrary\u002Fctypes.html) to call Go functions from the the awesome.so shared library as shown in the following snippet (some print statements are omitted).\n\nFile [client.py](.\u002Fclient.py)\n```python\nfrom __future__ import print_function\nfrom ctypes import *\n\nlib = cdll.LoadLibrary(\".\u002Fawesome.so\")\n\n# describe and invoke Add()\nlib.Add.argtypes = [c_longlong, c_longlong]\nlib.Add.restype = c_longlong\nprint(\"awesome.Add(12,99) = %d\" % lib.Add(12,99))\n\n# describe and invoke Cosine()\nlib.Cosine.argtypes = [c_double]\nlib.Cosine.restype = c_double\nprint(\"awesome.Cosine(1) = %f\" % lib.Cosine(1))\n\n# define class GoSlice to map to:\n# C type struct { void *data; GoInt len; GoInt cap; }\nclass GoSlice(Structure):\n    _fields_ = [(\"data\", POINTER(c_void_p)), (\"len\", c_longlong), (\"cap\", c_longlong)]\n\nnums = GoSlice((c_void_p * 5)(74, 4, 122, 9, 12), 5, 5) \n\n# call Sort\nlib.Sort.argtypes = [GoSlice]\nlib.Sort.restype = None\nlib.Sort(nums)\nprint(\"awesome.Sort(74,4,122,9,12) = %s\" % [nums.data[i] for i in range(nums.len)])\n\n# define class GoString to map:\n# C type struct { const char *p; GoInt n; }\nclass GoString(Structure):\n    _fields_ = [(\"p\", c_char_p), (\"n\", c_longlong)]\n\n# describe and call Log()\nlib.Log.argtypes = [GoString]\nlib.Log.restype = c_longlong\nmsg = GoString(b\"Hello Python!\", 13)\nprint(\"log id %d\"% lib.Log(msg))\n```\nNote 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:\n```shell\n$> python client.py\nawesome.Add(12,99) = 111\nawesome.Cosine(1) = 0.540302\nawesome.Sort(74,4,122,9,12) = [4, 9, 12, 74, 122]\nHello Python!\nlog id 1\n```\n\n### Python CFFI (contributed)\nThe following example was contributed by [@sbinet](https:\u002F\u002Fgithub.com\u002Fsbinet) (thank you!)\n\nPython also has a portable CFFI library that works with Python2\u002FPython3\u002Fpypy unchanged.  The \nfollowing example uses a C-wrapper to defined the exported Go types.  This makes\nthe python example less opaque and even easier to understand.\n\nFile [client-cffi.py](.\u002Fclient-cffi.py)\n```python\nfrom __future__ import print_function\nimport sys\nfrom cffi import FFI\n\nis_64b = sys.maxsize > 2**32\n\nffi = FFI()\nif is_64b: ffi.cdef(\"typedef long GoInt;\\n\")\nelse:      ffi.cdef(\"typedef int GoInt;\\n\")\n\nffi.cdef(\"\"\"\ntypedef struct {\n    void* data;\n    GoInt len;\n    GoInt cap;\n} GoSlice;\n\ntypedef struct {\n    const char *data;\n    GoInt len;\n} GoString;\n\nGoInt Add(GoInt a, GoInt b);\ndouble Cosine(double v);\nvoid Sort(GoSlice values);\nGoInt Log(GoString str);\n\"\"\")\n\nlib = ffi.dlopen(\".\u002Fawesome.so\")\n\nprint(\"awesome.Add(12,99) = %d\" % lib.Add(12,99))\nprint(\"awesome.Cosine(1) = %f\" % lib.Cosine(1))\n\ndata = ffi.new(\"GoInt[]\", [74,4,122,9,12])\nnums = ffi.new(\"GoSlice*\", {'data':data, 'len':5, 'cap':5})\nlib.Sort(nums[0])\nprint(\"awesome.Sort(74,4,122,9,12) = %s\" % [\n    ffi.cast(\"GoInt*\", nums.data)[i] \n    for i in range(nums.len)])\n\ndata = ffi.new(\"char[]\", b\"Hello Python!\")\nmsg = ffi.new(\"GoString*\", {'data':data, 'len':13})\nprint(\"log id %d\" % lib.Log(msg[0]))\n```\n\n\n## From Ruby\nCalling Go functions from Ruby follows a similar pattern as above. We use the the [FFI gem](https:\u002F\u002Fgithub.com\u002Fffi\u002Fffi) to dynamically load and call exported Go functions in the awesome.so shared object file as shown in the following snippet.\n\nFile [client.rb](.\u002Fclient.rb)\n```ruby\nrequire 'ffi'\n\n# Module that represents shared lib\nmodule Awesome\n  extend FFI::Library\n  \n  ffi_lib '.\u002Fawesome.so'\n  \n  # define class GoSlice to map to:\n  # C type struct { void *data; GoInt len; GoInt cap; }\n  class GoSlice \u003C FFI::Struct\n    layout :data,  :pointer,\n           :len,   :long_long,\n           :cap,   :long_long\n  end\n\n  # define class GoString to map:\n  # C type struct { const char *p; GoInt n; }\n  class GoString \u003C FFI::Struct\n    layout :p,     :pointer,\n           :len,   :long_long\n  end\n\n  # foreign function definitions\n  attach_function :Add, [:long_long, :long_long], :long_long\n  attach_function :Cosine, [:double], :double\n  attach_function :Sort, [GoSlice.by_value], :void\n  attach_function :Log, [GoString.by_value], :int\nend\n\n# Call Add\nprint \"awesome.Add(12, 99) = \",  Awesome.Add(12, 99), \"\\n\"\n\n# Call Cosine\nprint \"awesome.Cosine(1) = \", Awesome.Cosine(1), \"\\n\"\n\n# call Sort\nnums = [92,101,3,44,7]\nptr = FFI::MemoryPointer.new :long_long, nums.size\nptr.write_array_of_long_long  nums\nslice = Awesome::GoSlice.new\nslice[:data] = ptr\nslice[:len] = nums.size\nslice[:cap] = nums.size\nAwesome.Sort(slice)\nsorted = slice[:data].read_array_of_long_long nums.size\nprint \"awesome.Sort(\", nums, \") = \", sorted, \"\\n\"\n\n# Call Log\nmsg = \"Hello Ruby!\"\ngostr = Awesome::GoString.new\ngostr[:p] = FFI::MemoryPointer.from_string(msg) \ngostr[:len] = msg.size\nprint \"logid \", Awesome.Log(gostr), \"\\n\"\n```\nIn 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:\n```shell\n$> ruby client.rb\nawesome.Add(12, 99) = 111\nawesome.Cosine(1) = 0.5403023058681398\nawesome.Sort([92, 101, 3, 44, 7]) = [3, 7, 44, 92, 101]\nHello Ruby!\n```\n## From Node\nFor Node, we use a foreign function library called [node-ffi](https:\u002F\u002Fgithub.com\u002Fnode-ffi\u002Fnode-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:\n\nFile [client.js](.\u002Fclient.js)\n```js\nvar ref = require(\"ref\");\nvar ffi = require(\"ffi\");\nvar Struct = require(\"ref-struct\")\nvar ArrayType = require(\"ref-array\")\n\nvar longlong = ref.types.longlong;\nvar LongArray = ArrayType(longlong);\n\n\u002F\u002F define object GoSlice to map to:\n\u002F\u002F C type struct { void *data; GoInt len; GoInt cap; }\nvar GoSlice = Struct({\n  data: LongArray,\n  len:  \"longlong\",\n  cap: \"longlong\"\n});\n\n\u002F\u002F define object GoString to map:\n\u002F\u002F C type struct { const char *p; GoInt n; }\nvar GoString = Struct({\n  p: \"string\",\n  n: \"longlong\"\n});\n\n\u002F\u002F define foreign functions\nvar awesome = ffi.Library(\".\u002Fawesome.so\", {\n  Add: [\"longlong\", [\"longlong\", \"longlong\"]],\n  Cosine: [\"double\", [\"double\"]], \n  Sort: [\"void\", [GoSlice]],\n  Log: [\"longlong\", [GoString]]\n});\n\n\u002F\u002F call Add\nconsole.log(\"awesome.Add(12, 99) = \", awesome.Add(12, 99));\n\n\u002F\u002F call Cosine\nconsole.log(\"awesome.Cosine(1) = \", awesome.Cosine(1));\n\n\u002F\u002F call Sort\nnums = LongArray([12,54,0,423,9]);\nvar slice = new GoSlice();\nslice[\"data\"] = nums;\nslice[\"len\"] = 5;\nslice[\"cap\"] = 5;\nawesome.Sort(slice);\nconsole.log(\"awesome.Sort([12,54,9,423,9] = \", nums.toArray());\n\n\u002F\u002F call Log\nstr = new GoString();\nstr[\"p\"] = \"Hello Node!\";\nstr[\"n\"] = 11;\nawesome.Log(str);\n```\nNode 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:\n```shell\nawesome.Add(12, 99) =  111\nawesome.Cosine(1) =  0.5403023058681398\nawesome.Sort([12,54,9,423,9] =  [ 0, 9, 12, 54, 423 ]\nHello Node!\n```\n## From Java\nTo call the exported Go functions from Java, we use the [Java Native Access library](https:\u002F\u002Fgithub.com\u002Fjava-native-access\u002Fjna) or JNA as shown in the following code snippet (with some statements omitted or abbreviated):\n\nFile [Client.java](.\u002FClient.java)\n```java\nimport com.sun.jna.*;\nimport java.util.*;\nimport java.lang.Long;\n\npublic class Client {\n   public interface Awesome extends Library {\n        \u002F\u002F GoSlice class maps to:\n        \u002F\u002F C type struct { void *data; GoInt len; GoInt cap; }\n        public class GoSlice extends Structure {\n            public static class ByValue extends GoSlice implements Structure.ByValue {}\n            public Pointer data;\n            public long len;\n            public long cap;\n            protected List getFieldOrder(){\n                return Arrays.asList(new String[]{\"data\",\"len\",\"cap\"});\n            }\n        }\n\n        \u002F\u002F GoString class maps to:\n        \u002F\u002F C type struct { const char *p; GoInt n; }\n        public class GoString extends Structure {\n            public static class ByValue extends GoString implements Structure.ByValue {}\n            public String p;\n            public long n;\n            protected List getFieldOrder(){\n                return Arrays.asList(new String[]{\"p\",\"n\"});\n            }\n\n        }\n\n        \u002F\u002F Foreign functions\n        public long Add(long a, long b);\n        public double Cosine(double val);\n        public void Sort(GoSlice.ByValue vals);\n        public long Log(GoString.ByValue str);\n    }\n \n   static public void main(String argv[]) {\n        Awesome awesome = (Awesome) Native.loadLibrary(\n            \".\u002Fawesome.so\", Awesome.class);\n\n        System.out.printf(\"awesome.Add(12, 99) = %s\\n\", awesome.Add(12, 99));\n        System.out.printf(\"awesome.Cosine(1.0) = %s\\n\", awesome.Cosine(1.0));\n        \n        \u002F\u002F Call Sort\n        \u002F\u002F First, prepare data array \n        long[] nums = new long[]{53,11,5,2,88};\n        Memory arr = new Memory(nums.length * Native.getNativeSize(Long.TYPE));\n        arr.write(0, nums, 0, nums.length); \n        \u002F\u002F fill in the GoSlice class for type mapping\n        Awesome.GoSlice.ByValue slice = new Awesome.GoSlice.ByValue();\n        slice.data = arr;\n        slice.len = nums.length;\n        slice.cap = nums.length;\n        awesome.Sort(slice);\n        System.out.print(\"awesome.Sort(53,11,5,2,88) = [\");\n        long[] sorted = slice.data.getLongArray(0,nums.length);\n        for(int i = 0; i \u003C sorted.length; i++){\n            System.out.print(sorted[i] + \" \");\n        }\n        System.out.println(\"]\");\n\n        \u002F\u002F Call Log\n        Awesome.GoString.ByValue str = new Awesome.GoString.ByValue();\n        str.p = \"Hello Java!\";\n        str.n = str.p.length();\n        System.out.printf(\"msgid %d\\n\", awesome.Log(str));\n\n    }\n}\n```\nTo 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:\n```shell\n$> javac -cp jna.jar Client.java\n$> java -cp .:jna.jar Client\nawesome.Add(12, 99) = 111\nawesome.Cosine(1.0) = 0.5403023058681398\nawesome.Sort(53,11,5,2,88) = [2 5 11 53 88 ]\nHello Java!\n```\n## From Lua (contributed)\nThis example was contributed by [@titpetric](https:\u002F\u002Fgithub.com\u002Ftitpetric). See his insightful write up on [*Calling Go functions from LUA*](https:\u002F\u002Fscene-si.org\u002F2017\u002F03\u002F13\u002Fcalling-go-functions-from-lua\u002F).\n\nThe 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.\n\nFile [client.lua](.\u002Fclient.lua)\n```lua\nlocal ffi = require(\"ffi\")\nlocal awesome = ffi.load(\".\u002Fawesome.so\")\n\nffi.cdef([[\ntypedef long long GoInt64;\ntypedef unsigned long long GoUint64;\ntypedef GoInt64 GoInt;\ntypedef GoUint64 GoUint;\ntypedef double GoFloat64;\n\ntypedef struct { const char *p; GoInt n; } GoString;\ntypedef struct { void *data; GoInt len; GoInt cap; } GoSlice;\n\nextern GoInt Add(GoInt p0, GoInt p1);\nextern GoFloat64 Cosine(GoFloat64 p0);\nextern void Sort(GoSlice p0);\nextern GoInt Log(GoString p0);\n]]);\n\n\nio.write( string.format(\"awesome.Add(12, 99) = %f\\n\", math.floor(tonumber(awesome.Add(12,99)))) )\n\nio.write( string.format(\"awesome.Cosine(1) = %f\\n\", tonumber(awesome.Cosine(1))) )\n\nlocal nums = ffi.new(\"long long[5]\", {12,54,0,423,9})\nlocal numsPointer = ffi.new(\"void *\", nums);\nlocal typeSlice = ffi.metatype(\"GoSlice\", {})\nlocal slice = typeSlice(numsPointer, 5, 5)\nawesome.Sort(slice)\n\nio.write(\"awesome.Sort([12,54,9,423,9] = \")\nfor i=0,4 do\n\tif i > 0 then\n\t\tio.write(\", \")\n\tend\n\tio.write(tonumber(nums[i]))\nend\nio.write(\"\\n\");\n\nlocal typeString = ffi.metatype(\"GoString\", {})\nlocal logString = typeString(\"Hello LUA!\", 10)\nawesome.Log(logString)\n```\nWhen the example is executed, it produces the following:\n```\n$> luajit client.lua\nawesome.Add(12, 99) = 111.000000\nawesome.Cosine(1) = 0.540302\nawesome.Sort([12,54,9,423,9] = 0, 9, 12, 54, 423\nHello LUA!\n```\n## From Julia (Contributed)\nThe following example was contributed by [@r9y9](https:\u002F\u002Fgithub.com\u002Fr9y9). It shows how to invoke exported Go functions from the Julia language. As [documented here](https:\u002F\u002Fdocs.julialang.org\u002Fen\u002Fstable\u002Fmanual\u002Fcalling-c-and-fortran-code\u002F), Julia has the capabilities to invoke exported functions from shared libraries similar to other languages discussed here.\n\nFile [client.jl](.\u002Fclient.jl)\n```julia\nstruct GoSlice\n    arr::Ptr{Void}\n    len::Int64\n    cap::Int64\nend\nGoSlice(a::Vector, cap=10) = GoSlice(pointer(a), length(a), cap)\n\nstruct GoStr\n    p::Ptr{Cchar}\n    len::Int64\nend\nGoStr(s::String) = GoStr(pointer(s), length(s))\n\nconst libawesome = \"awesome.so\"\n\nAdd(x,y) = ccall((:Add, libawesome), Int,(Int,Int), x,y)\nCosine(x) = ccall((:Cosine, libawesome), Float64, (Float64,), x)\nfunction Sort(vals)\n    ccall((:Sort, libawesome), Void, (GoSlice,), GoSlice(vals))\n    return vals # for convenience\nend\nLog(msg) = ccall((:Log, libawesome), Int, (GoStr,), GoStr(msg))\n\nfor ex in [:(Add(12, 9)),:(Cosine(1)), :(Sort([77,12,5,99,28,23]))]\n    println(\"awesome.$ex = $(eval(ex))\")\nend\nLog(\"Hello from Julia!\")\n```\nWhen the example is executed, it produces the following:\n\n```\n> julia client.jl\nawesome.Add(12, 9) = 21\nawesome.Cosine(1) = 0.5403023058681398\nawesome.Sort([77, 12, 5, 99, 28, 23]) = [5, 12, 23, 28, 77, 99]\nHello from Julia!\n```\n## From Dart (Contributed)\nThe following example was contributed by @dpurfield. It shows how to invoke exported Go functions from the Dart language. As documented [here](https:\u002F\u002Fdart.dev\u002Fguides\u002Flibraries\u002Fc-interop), Dart has the capability to invoke exported functions from shared libraries similar to other languages discussed here.\n\nFile [client.dart](.\u002Fclient.dart)\n```dart\nimport 'dart:convert';\nimport 'dart:ffi';\nimport 'dart:io';\n\nclass GoSlice extends Struct\u003CGoSlice> {\n  Pointer\u003CInt64> data;\n\n  @Int64()\n  int len;\n\n  @Int64()\n  int cap;\n\n  List\u003Cint> toList() {\n    List\u003Cint> units = [];\n    for (int i = 0; i \u003C len; ++i) {\n      units.add(data.elementAt(i).load\u003Cint>());\n    }\n    return units;\n  }\n\n  static Pointer\u003CGoSlice> fromList(List\u003Cint> units) {\n    final ptr = Pointer\u003CInt64>.allocate(count: units.length);\n    for (int i =0; i \u003C units.length; ++i) {\n      ptr.elementAt(i).store(units[i]);\n    }\n    final GoSlice slice = Pointer\u003CGoSlice>.allocate().load();\n    slice.data = ptr;\n    slice.len = units.length;\n    slice.cap = units.length;\n    return slice.addressOf;\n  }\n}\n\nclass GoString extends Struct\u003CGoString> {\n  Pointer\u003CUint8> string;\n\n  @IntPtr()\n  int length;\n\n  String toString() {\n    List\u003Cint> units = [];\n    for (int i = 0; i \u003C length; ++i) {\n      units.add(string.elementAt(i).load\u003Cint>());\n    }\n    return Utf8Decoder().convert(units);\n  }\n\n  static Pointer\u003CGoString> fromString(String string) {\n    List\u003Cint> units = Utf8Encoder().convert(string);\n    final ptr = Pointer\u003CUint8>.allocate(count: units.length);\n    for (int i = 0; i \u003C units.length; ++i) {\n      ptr.elementAt(i).store(units[i]);\n    }\n    final GoString str = Pointer\u003CGoString>.allocate().load();\n    str.length = units.length;\n    str.string = ptr;\n    return str.addressOf;\n  }\n}\n\ntypedef add_func = Int64 Function(Int64, Int64);\ntypedef Add = int Function(int, int);\ntypedef cosine_func = Double Function(Double);\ntypedef Cosine = double Function(double);\ntypedef log_func = Int64 Function(Pointer\u003CGoString>);\ntypedef Log = int Function(Pointer\u003CGoString>);\ntypedef sort_func = Void Function(Pointer\u003CGoSlice>);\ntypedef Sort = void Function(Pointer\u003CGoSlice>);\n\nvoid main(List\u003CString> args) {\n\n  final awesome = DynamicLibrary.open('awesome.so');\n\n  final Add add = awesome.lookup\u003CNativeFunction\u003Cadd_func>>('Add').asFunction();\n  stdout.writeln(\"awesome.Add(12, 99) = ${add(12, 99)}\");\n\n  final Cosine cosine = awesome.lookup\u003CNativeFunction\u003Ccosine_func>>('Cosine').asFunction();\n  stdout.writeln(\"awesome.Cosine(1) = ${cosine(1.0)}\");\n\n  final Log log = awesome.lookup\u003CNativeFunction\u003Clog_func>>('LogPtr').asFunction();\n  final Pointer\u003CGoString> message = GoString.fromString(\"Hello, Dart!\");\n  try {\n    log(message);\n  }\n  finally {\n    message.free();\n  }\n\n  final Sort sort = awesome.lookup\u003CNativeFunction\u003Csort_func>>('SortPtr').asFunction();\n  var nums = [12,54,0,423,9];\n  final Pointer\u003CGoSlice> slice = GoSlice.fromList(nums);\n  try {\n    sort(slice);\n    stdout.writeln(slice.load\u003CGoSlice>().toList());\n  } finally {\n    slice.free();\n  }\n\n  for (int i=0; i \u003C 100000; i++) {\n    Pointer\u003CGoString> m = GoString.fromString(\"Hello, Dart!\");\n    Pointer\u003CGoSlice> s = GoSlice.fromList(nums);\n    print(\"$m $s\");\n    m.free();\n    s.free();\n  }\n\n  stdin.readByteSync();\n}\n```\n## From C#\nTo call the exported Go functions from C# we use the [DllImportAttribute](https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fsystem.runtime.interopservices.dllimportattribute?view=netframework-4.8) attribute to dynamically load and call exported Go functions in the awesome.so shared object file as shown in the following snippet.\n\nFile [client.cs](.\u002Fclient.cs)\n```cs\nusing System;\nusing System.Runtime.InteropServices;\n\nclass Awesome\n{\n    const string libName = \"awesome.so\";\n\n    public struct GoSlice\n    {\n        public IntPtr data;\n        public long len, cap;\n        public GoSlice(IntPtr data, long len, long cap)\n        {\n            this.data = data;\n            this.len = len;\n            this.cap = cap;\n        }\n    }\n    public struct GoString\n    {\n        public string msg;\n        public long len;\n        public GoString(string msg, long len)\n        {\n            this.msg = msg;\n            this.len = len;\n        }\n    }\n\n    \u002F\u002F Use DllImport to import the Awesome lib.\n    [DllImport(libName)]\n    public static extern int Add(long a, long b);\n\n    [DllImport(libName)]\n    public static extern double Cosine(double a);\n\n    [DllImport(libName)]\n    public static extern void Sort(GoSlice a);\n\n    [DllImport(libName, CharSet = CharSet.Unicode)]\n    public static extern void Log(GoString msg);\n\n    static void Main()\n    {\n        long add = Add(12, 99);\n        double cosine = Cosine(1);\n\n        long[] data = { 77, 12, 5, 99, 28, 23 };\n        IntPtr data_ptr = Marshal.AllocHGlobal(Buffer.ByteLength(data));\n        Marshal.Copy(data, 0, data_ptr, data.Length);\n        var nums = new GoSlice(data_ptr, data.Length, data.Length);\n        Sort(nums);\n        Marshal.Copy(nums.data, data, 0, data.Length);\n\n        string msg = \"Hello from C#!\";\n        GoString str = new GoString(msg, msg.Length);\n\n        Console.WriteLine(\"awesome.Add(12,99) = \" + add);\n        Console.WriteLine(\"awesome.Cosine(1) = \" + cosine);\n        Console.WriteLine(\"awesome.Sort(77,12,5,99,28,23): \" + string.Join(\", \", data));\n        Log(str);\n    }\n}\n```\nWhen the example is executed, it produces the following:\n\n```\n> dotnet run\nawesome.Add(12,99) = 111\nawesome.Cosine(1) = 0,5403023058681398\nawesome.Sort(77,12,5,99,28,23): 5, 12, 23, 28, 77, 99\nHello from C#!\n```\n\n## From Zig (Contributed)\n\nThe following example was contributed by @dgv. It shows how to invoke exported Go functions from the Zig language. As documented [here](https:\u002F\u002Fziglang.org\u002Fdocumentation\u002Fmaster\u002F#C), Zig has the natural capability to interop with C and invoke exported functions from shared libraries not depending on libc.\n\nThere are two ways to use the shared object library to call Go functions from Zig like 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.\n\n### Dynamically linked\n\nIn 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:\n\nFile [client1.zig](.\u002Fclient1.zig)\n\n```zig\nconst std = @import(\"std\");\nconst awesome = @cImport({\n    @cInclude(\"awesome.h\");\n});\n\npub fn main() !void {\n    \u002F\u002FCall Add() - passing integer params, interger result\n    const a: awesome.GoInt = 12;\n    const b: awesome.GoInt = 99;\n    std.debug.print(\"awesome.Add(12,99) = {d}\\n\", .{awesome.Add(a, b)});\n\n    \u002F\u002FCall Cosine() - passing float param, float returned\n    std.debug.print(\"awesome.Cosine(1) = {d}\\n\", .{awesome.Cosine(1.0)});\n\n    \u002F\u002FCall SortPtr() - converting the array and passing as GoSlice pointer\n    const data = [6]awesome.GoInt{ 77, 12, 5, 99, 28, 23 };\n    const nums = awesome.GoSlice{ .data = @ptrCast(@constCast(&data)), .len = data.len, .cap = data.len };\n    awesome.SortPtr(@ptrCast(@constCast(&nums)));\n    std.debug.print(\"awesome.SortPtr(77,12,5,99,28,23): \", .{});\n    for (@as(*align(1) const [nums.len]awesome.GoInt, @ptrCast(nums.data))) |n| {\n        std.debug.print(\"{d},\", .{n});\n    }\n    std.debug.print(\"\\n\", .{});\n\n    \u002F\u002FCall Log() - passing string value\n    const msg = awesome.GoString{ .p = \"Hello from Zig!\", .n = 16 };\n    _ = awesome.Log(msg);\n}\n```\n\nNext we use Zig to compile and run the code, specifying the shared object library, it links to the awesome.so library, calling the functions that were exported from Go as the output shows below.\n\n```shell\n$> zig run client1.zig -I. .\u002Fawesome.so\nawesome.Add(12,99) = 111\nawesome.Cosine(1) = 0.5403023058681398\nawesome.SortPtr(77,12,5,99,28,23): 5,12,23,28,77,99,\nHello from Zig!\n```\n\n### Dynamically Loaded\n\nIn this approach, the Zig code uses the dynamic library loading to dynamically load and bind exported symbols. It uses functions defined in [DynLib](https:\u002F\u002Fziglang.org\u002Fdocumentation\u002Fmaster\u002Fstd\u002F#std.dynamic_library.DynLib) such as `open` to open the library file, `lookup` to look up a symbol, and `close` to close the shared library file.\n\nBecause 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).\n\nFile [client2.zig](.\u002Fclient2.zig)\n\n```zig\nconst std = @import(\"std\");\n\nconst go_int = i64;\nconst go_float = f64;\nconst go_str = []const u8;\nconst go_slice = extern struct {\n    data: *[]go_int,\n    len: go_int,\n    cap: go_int,\n};\nvar Add: *const fn (a: go_int, b: go_int) go_int = undefined;\nvar Cosine: *const fn (n: go_float) go_float = undefined;\nvar SortPtr: *const fn (nums: *go_slice) void = undefined;\nvar Log: *const fn (str: go_str) go_int = undefined;\n\npub fn main() !void {\n    \u002F\u002F use dlopen to load shared object\n    var awesome = try std.DynLib.open(\".\u002Fawesome.so\");\n    \u002F\u002F close file handle when done\n    defer awesome.close();\n\n    \u002F\u002F resolve Add symbol and assign to fn ptr\n    Add = awesome.lookup(@TypeOf(Add), \"Add\") orelse return error.LookupFail;\n    \u002F\u002F call Add()\n    std.debug.print(\"awesome.Add(12,99) = {d}\\n\", .{Add(12, 99)});\n\n    \u002F\u002F resolve Cosine symbol\n    Cosine = awesome.lookup(@TypeOf(Cosine), \"Cosine\") orelse return error.LookupFail;\n    \u002F\u002F Call Cosine\n    std.debug.print(\"awesome.Cosine(1) = {d}\\n\", .{Cosine(1.0)});\n\n    \u002F\u002F resolve SortPtr symbol\n    SortPtr = awesome.lookup(@TypeOf(SortPtr), \"SortPtr\") orelse return error.LookupFail;\n    const data = [5]go_int{ 44, 23, 7, 66, 2 };\n    const nums = go_slice{ .data = @ptrCast(@constCast(&data)), .len = data.len, .cap = data.len };\n    \u002F\u002F call SortPtr\n    SortPtr(@ptrCast(@constCast(&nums)));\n    std.debug.print(\"awesome.SortPtr(44,23,7,66,2): \", .{});\n    for (@as(*align(1) const [nums.len]go_int, @ptrCast(nums.data))) |n| {\n        std.debug.print(\"{d},\", .{n});\n    }\n    std.debug.print(\"\\n\", .{});\n\n    \u002F\u002F resolve Log symbol\n    Log = awesome.lookup(@TypeOf(Log), \"Log\") orelse return error.LookupFail;\n    \u002F\u002F call Log\n    _ = Log(\"Hello from Zig!\");\n}\n```\n\nIn the previous code, we define our own subset of Go compatible C types `go_int`, `go_float`, `go_slice`, and `go_str`. We use `lookup` 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:\nWhen the code is executed, the C binary loads and links to shared library awesome.so producing the following output:\n\n```shell\n$> zig run client2.zig .\u002Fawesome.so\nawesome.Add(12,99) = 111\nawesome.Cosine(1) = 0.5403023058681398\nawesome.SortPtr(44,23,7,66,2): 2,7,23,44,66,\nHello from Zig!\n```\n\n## Conclusion\nThis 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.\n","该项目展示了如何通过C共享库从其他语言调用Go函数。核心功能是利用`-buildmode=c-shared`编译选项将Go代码编译成C风格的API，生成标准的共享对象文件（.so），使得Go编写的库可以被C、Python、Ruby、Node.js、Java等语言直接调用。技术特点包括需要在Go代码中导入\"C\"伪包，并使用\u002F\u002Fexport注释标记要导出的函数。此外，项目还提供了详细的示例说明如何编写满足要求的Go代码以及如何编译这些代码。适用于希望复用Go代码但主要开发环境为其他语言的场景，如跨语言开发或已有系统集成Go模块的需求。",2,"2026-06-11 03:23:11","top_language"]