Вадим Марковцев, source{d}
vmarkovtsev
markhor
go get -u gopkg.in/src-d/go-git.v3
r, _ := git.NewRepository("https://github.com/...", nil)_ = r.PullDefault()iter, _ := r.Commits()for {commit, err := iter.Next()if err == io.EOF breakfmt.Println(commit)}
go list -f '{{ .Deps }}' github.com/src-d/go-git
package main/* #include <stdio.h>void cprint(const char *what) {printf("%s", what);} */import "C"func main() {C.cprint(C.CString("Hello, world!\n"))}
$ CC=wat go build test.go# command-line-argumentsexec: "wat": executable file not found in $PATH$ nm test|grep cprint0000000000454120 T _cgo_efd028656a81_Cfunc_cprint0000000000401510 t main._Cfunc_cprint00000000006c2130 d main._cgo_efd028656a81_Cfunc_cprint0000000000454110 T cprint
Компиляция выполняется хостовым CC и затем объектник линкуется
main._Cfunc_cprint ==
вызов C.print в коде mainruntime.cgocall(_cgo_efd028656a81_Cfunc_cprint, frame)cprint с переданными параметрамиcprintcprintvoid ...(const char *what) {printf("%s", what);free(what);}
/* #include <stdlib.h> */import "C"func main() {C.free(C.CString("..."))}
go build -o libtest.so -buildmode=c-shared test.go
package mainimport "C"//export goprintfunc goprint(what string) { print(what) }func main() { print("main\n") }
libtest.h libtest.so
$ nm libtest.so|grep goprint0000000000061270 T _cgoexp_25e34a201b27_goprint00000000000b4610 T goprint00000000000612b0 t main._cgoexpwrap_25e34a201b27_goprint00000000002fa428 d main._cgoexpwrap_25e34a201b27_goprint.f$ gdb ./testb goprint
gcc test.c -Wl,-rpath,. -L. -ltest -o test
#include "libtest.h"int main() {GoString str = {"Hello, world!\n", 14};goprint(str);return 0;}
GoString (см. первый доклад)goprint, которая находится в libtest.so_cgoexp_25e34a201b27_goprint через как-бы syscallruntime.cgocallback с параметром main._cgoexpwrap_25e34a201b27_goprint.fmain._cgoexpwrap_25e34a201b27_goprintint mainGo type not supported in export: struct
var hello string = "hello"//export givemefunc giveme() *string {return &hello}
#include <stdio.h>#include "libtest.h"int main() {GoString *str = giveme();printf("%s\n", str->p);return 0;}
$ ./testpanic: runtime error: cgo result has Go pointergoroutine 17 [running, locked to thread]:panic(0x7fd4d5a808a0, 0xc82007c020)/usr/lib/go-1.6/src/runtime/panic.go:481 +0x3eamain._cgoexpwrap_b3226aa49989_giveme.func1(0xc82003cee0)command-line-arguments/_obj/_cgo_gotypes.go:49 +0x3cmain._cgoexpwrap_b3226aa49989_giveme(0x7fd4d5ad6160)command-line-arguments/_obj/_cgo_gotypes.go:51 +0x5bAborted (core dumped)
import cffi, reffi = cffi.FFI()with open("libtest.h") as fin:src = fin.read()
src = re.sub("#ifdef.*\n.*\n#endif|#.*|.*_Complex.*|"".*_check_for_64_bit_pointer_matching_GoInt.*","", src)src = "extern free(void *ptr);\n" + \src.replace("__SIZE_TYPE__", "uintptr_t")
ffi.cdef(src)lib = ffi.dlopen("./libtest.so")
python_str = "Hello!\n".encode("utf-8")char_ptr = ffi.new("char[]", python_str)go_str = ffi.new("GoString*", {"p": char_ptr,"n": len(python_str)})[0]
lib.goprint(go_str)
Видите как все просто!
del char_ptr - заморочки с местным GC
О да!
Что же делать, ведь указатели запрещены?
Идея: поддерживать двусторонний мэппинг объектов на 64-битные дескрипторы
func RegisterObject(obj interface{}) Handlefunc UnregisterObject(handle Handle) intfunc GetObject(handle Handle) (interface{}, bool)func GetHandle(obj interface{}) (Handle, bool)
Важно: за исключением редких случаев, всегда используем указатель на структуру в качествеtype Handle uint64var registryHandle2Obj map[Handle]interface{}var registryObj2Handle ???
interface{}, т.к. иначе будет копия.
map.
*interface{} - подумайте еще раз[2]uintptr может быть ключомuintptr - указателем на данные?var registryObj2Handle map[uintptr][]Handle
Исходник.
string из внешнего мира, то он заразит собой каждую
сущность которая с ней встретитсяfree()Лечение состоит в принудительном копировании.
[]byte в
*C.charclass GoObject(object):ffi = FFI()lib = Noneregistry = weakref.WeakValueDictionary()def __new__(cls, handle, *args, **kwargs):assert cls.lib is not Noneinstance = GoObject.registry.get(handle)if instance is not None:return instancereturn object.__new__(cls)
def __init__(self, handle):assert isinstance(handle, int)if handle <= 0:raise ValueError("Invalid handle")self._handle = handleGoObject.registry[handle] = selfdef __del__(self):handle = getattr(self, "_handle", None)if handle is not None:self.lib.UnregisterObject(handle)self._handle = 0
from gypogit import Repositoryr = Repository.New("https://github.com/...")r.PullDefault()for c in r.Commits():print(c)