Вадим Марковцев, source{d}
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 break
fmt.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-arguments
exec: "wat": executable file not found in $PATH
$ nm test|grep cprint
0000000000454120 T _cgo_efd028656a81_Cfunc_cprint
0000000000401510 t main._Cfunc_cprint
00000000006c2130 d main._cgo_efd028656a81_Cfunc_cprint
0000000000454110 T cprint
Компиляция выполняется хостовым CC и затем объектник линкуется
main._Cfunc_cprint
==
вызов C.print
в коде mainruntime.cgocall(_cgo_efd028656a81_Cfunc_cprint, frame)
cprint
с переданными параметрамиcprint
cprint
void ...(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 main
import "C"
//export goprint
func goprint(what string) { print(what) }
func main() { print("main\n") }
libtest.h libtest.so
$ nm libtest.so|grep goprint
0000000000061270 T _cgoexp_25e34a201b27_goprint
00000000000b4610 T goprint
00000000000612b0 t main._cgoexpwrap_25e34a201b27_goprint
00000000002fa428 d main._cgoexpwrap_25e34a201b27_goprint.f
$ gdb ./test
b 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.f
main._cgoexpwrap_25e34a201b27_goprint
int main
Go type not supported in export: struct
var hello string = "hello"
//export giveme
func giveme() *string {
return &hello
}
#include <stdio.h>
#include "libtest.h"
int main() {
GoString *str = giveme();
printf("%s\n", str->p);
return 0;
}
$ ./test
panic: runtime error: cgo result has Go pointer
goroutine 17 [running, locked to thread]:
panic(0x7fd4d5a808a0, 0xc82007c020)
/usr/lib/go-1.6/src/runtime/panic.go:481 +0x3ea
main._cgoexpwrap_b3226aa49989_giveme.func1(0xc82003cee0)
command-line-arguments/_obj/_cgo_gotypes.go:49 +0x3c
main._cgoexpwrap_b3226aa49989_giveme(0x7fd4d5ad6160)
command-line-arguments/_obj/_cgo_gotypes.go:51 +0x5b
Aborted (core dumped)
import cffi, re
ffi = 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{}) Handle
func UnregisterObject(handle Handle) int
func GetObject(handle Handle) (interface{}, bool)
func GetHandle(obj interface{}) (Handle, bool)
Важно: за исключением редких случаев, всегда используем указатель на структуру в качествеtype Handle uint64
var registryHandle2Obj map[Handle]interface{}
var registryObj2Handle ???
interface{}
, т.к. иначе будет копия.
map
.
*interface{}
- подумайте еще раз[2]uintptr
может быть ключомuintptr
- указателем на данные?var registryObj2Handle map[uintptr][]Handle
Исходник.
string
из внешнего мира, то он заразит собой каждую
сущность которая с ней встретитсяfree()
Лечение состоит в принудительном копировании.
[]byte
в
*C.char
class GoObject(object):
ffi = FFI()
lib = None
registry = weakref.WeakValueDictionary()
def __new__(cls, handle, *args, **kwargs):
assert cls.lib is not None
instance = GoObject.registry.get(handle)
if instance is not None:
return instance
return object.__new__(cls)
def __init__(self, handle):
assert isinstance(handle, int)
if handle <= 0:
raise ValueError("Invalid handle")
self._handle = handle
GoObject.registry[handle] = self
def __del__(self):
handle = getattr(self, "_handle", None)
if handle is not None:
self.lib.UnregisterObject(handle)
self._handle = 0
from gypogit import Repository
r = Repository.New("https://github.com/...")
r.PullDefault()
for c in r.Commits():
print(c)