воскресенье, 14 июля 2019 г.

Команда cgo: ссылки Go к C

В файле Go к именам структурных полей C, которые являются ключевыми словами в Go, можно получить доступ, поставив перед ними префикс подчеркивания: если x указывает на структуру C с полем с именем "type", x._type получает доступ к полю. Поля структуры C, которые не могут быть выражены в Go, такие как битовые поля или неправильно выровненные данные, опускаются в структуре Go и заменяются соответствующим заполнением для достижения следующего поля или конца структуры.

Стандартные числовые типы C доступны под именами C.char, C.schar (char со знаком), C.uchar (char без знака), C.short, C.ushort (short без знака), C.int, C.uint (int без знака), C.long, C.ulong (long без знака), C.longlong (long long), C.ulonglong (long long без знака), C.float, C.double, C.complexfloat (complex float) и C.complexdouble (complex double). Тип C void* представлен небезопасным Go-указателем. Типы C __int128_t и __uint128_t представлены типом [16]byte.

Несколько специальных типов C, которые обычно представлены типом указателя в Go, вместо этого представлены uintptr.

Чтобы получить прямой доступ к типу struct, union или enum, поставьте перед ним префикс struct_, union_ или enum_, как в C.struct_stat.

Размер любого типа C доступен как C.sizeof_T, как и в C.sizeof_struct_stat.

Функция C может быть объявлена в файле Go с типом параметра со специальным именем _GoString_. Эта функция может вызываться с обычным значением строки Go. Длина строки и указатель на содержимое строки могут быть доступны путем вызова функций C

size_t _GoStringLen(_GoString_ s);
const char *_GoStringPtr(_GoString_ s);

Эти функции доступны только в преамбуле, но не в других файлах C. Код C не должен изменять содержимое указателя, возвращаемого _GoStringPtr. Обратите внимание, что содержимое строки может не иметь завершающего байта NUL.

Поскольку Go не поддерживает тип C union в общем случае, типы C union представляются в виде байтового массива Go одинаковой длины.

Структуры Go не могут вставлять поля с типами C.

Код Go не может ссылаться на поля нулевого размера, которые появляются в конце непустых структур C. Чтобы получить адрес такого поля (это единственная операция, которую вы можете выполнить с полем нулевого размера), вы должны взять адрес структуры и добавить размер структуры.

Cgo переводит типы C в эквивалентные неэкспортированные типы Go. Поскольку переводы не экспортируются, пакет Go не должен предоставлять типы C в своем экспортированном API: тип C, используемый в одном пакете Go, отличается от того же типа C, который используется в другом.

Любая функция C (даже функции void) может быть вызвана в контексте множественного присваивания, чтобы получить как возвращаемое значение (если оно есть), так и переменную C errno как ошибку (используйте _, чтобы пропустить значение результата, если функция возвращает void). Например:

n, err = C.sqrt(-1)
_, err := C.voidFunc()
var n, err = C.sqrt(1)

Вызов указателей на функции C в настоящее время не поддерживается, однако вы можете объявить переменные Go, которые содержат указатели на функции C и передавать их между Go и C. Код C может вызывать указатели на функции, полученные из Go. Например:

package main

// typedef int (*intFunc) ();
//
// int
// bridge_int_func(intFunc f)
// {
//    return f();
// }
//
// int fortytwo()
// {
//      return 42;
// }
import "C"
import "fmt"
func main() {
  f := C.intFunc(C.fortytwo)
  fmt.Println(int(C.bridge_int_func(f)))
  // Output: 42
}

В C аргумент функции, записанный как массив фиксированного размера, фактически требует указатель на первый элемент массива. Компиляторы C знают об этом соглашении о вызовах и корректируют вызов соответствующим образом, но Go не может. В Go вы должны явно передать указатель на первый элемент: C.f(&C.x[0]).

Вызов переменных функций C (variadic C functions) не поддерживается. Это можно обойти, используя обертку функции C (C function wrapper). Например:

package main

// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
//   printf("%s\n", s);
// }
import "C"
import "unsafe"

func main() {
  cs := C.CString("Hello from stdio")
  C.myprint(cs)
  C.free(unsafe.Pointer(cs))
}

Несколько специальных функций конвертируют между типами Go и C, делая копии данных. В псевдо-Go определениях:

// Go строка в C строку
// Строка C размещается в куче C (C heap) с помощью malloc.
// Освобождение памяти это ответственность вызывающего, 
// например, вызвав C.free (обязательно включите stdlib.h
// если нужен C.free).
func C.CString(string) *C.char

// Go []byte срез в массив C (C array)
// Массив C размещается в куче C ( C heap) с помощью malloc.
// Освобождение памяти это ответственность вызывающего, 
// например, вызвав C.free (обязательно включите stdlib.h
// если нужен C.free).
func C.CBytes([]byte) unsafe.Pointer

// C строка в Go строку 
func C.GoString(*C.char) string

// Данные C с явной длиной в Go строку
func C.GoStringN(*C.char, C.int) string

// Данные C с явной длиной в Go []byte
func C.GoBytes(unsafe.Pointer, C.int) []byte

В особом случае C.malloc не вызывает напрямую библиотеку C malloc, а вместо этого вызывает вспомогательную функцию Go, которая упаковывает библиотеку C malloc, но гарантирует, что она никогда не вернет nil. Если malloc C указывает на нехватку памяти, вспомогательная функция завершает работу программы, например, когда самому Go не хватает памяти. Поскольку C.malloc не может потерпеть неудачу, у него нет формы с двумя результатами, которая возвращает errno.


Читайте также:


Комментариев нет:

Отправить комментарий