понедельник, 15 июля 2019 г.

Команда cgo: особые случаи

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

1. Типы *Ref для Darwin, основанные на типе CFTypeRef CoreFoundation.

2. Типы объектов из интерфейса JNI Java:

jobject
jclass
jthrowable
jstring
jarray
jbooleanArray
jbyteArray
jcharArray
jshortArray
jintArray
jlongArray
jfloatArray
jdoubleArray
jobjectArray
jweak

3. Тип EGLDisplay из API EGL.

Эти типы являются uintptr на стороне Go, потому что в противном случае они могли бы сбить с толку сборщик мусора Go; иногда это не указатели, а структуры данных, закодированные в виде указателя. Все операции над этими типами должны происходить в C. Подходящая константа для инициализации пустой такой ссылки - 0, а не nil.

Эти особые случаи были введены в Go 1.10. Для автоматического обновления кода из Go 1.9 и более ранних версий используйте cftype или jni перезаписи в инструменте go fix:

go tool fix -r cftype 
go tool fix -r jni 

Он заменит nil на 0 в соответствующих местах.

Случай EGLDisplay был представлен в Go 1.12. Используйте перезапись egl для автоматического обновления кода с Go 1.11 и более ранних версий:

go tool fix -r egl 


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


Команда cgo: передача указателей

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

В этом посте термин указатель Go означает указатель на память, выделенную Go (например, с помощью оператора & или вызвав предопределенную функцию new), а термин C указатель означает указатель на память, выделенную C (например, путем вызова C.malloc). Является ли указатель указателем Go или указателем C, в любом случае это динамическое свойство, определяемое тем, как была выделена память; это не имеет ничего общего с типом указателя.

Обратите внимание, что значения некоторых типов Go, кроме нулевого значения типа, всегда включают указатели Go. Это верно для типов строки, среза, интерфейса, канала, карты и функций. Тип указателя может содержать указатель Go или указатель C. Типы массивов и структур могут включать или не включать указатели Go в зависимости от типов элементов. Все обсуждения ниже по поводу указателей Go применяются не только к типам указателей, но и к другим типам, которые включают указатели Go.

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

Код C может не сохранять копию указателя Go после возврата вызова. Это включает в себя тип _GoString_, который, как отмечено выше, включает в себя указатель Go; Значения _GoString_ не могут быть сохранены кодом C.

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

Код Go не может хранить указатель Go в памяти C. Код C может хранить указатели Go в памяти C, в соответствии с приведенным выше правилом: он должен прекратить хранить указатель Go, когда функция C вернется.

Эти правила проверяются динамически во время выполнения. Проверка контролируется параметром cgocheck переменной среды GODEBUG. Значением по умолчанию является GODEBUG=cgocheck=1, которое реализует достаточно дешевые динамические проверки. Эти проверки могут быть полностью отключены с помощью GODEBUG=cgocheck=0. Полная проверка обработки указателя, за определенную плату во время выполнения, доступна через GODEBUG=cgocheck=2.

Можно победить это применение с помощью unsafe пакета, и, конечно, ничто не мешает коду C делать все, что ему нравится. Однако программы, нарушающие эти правила, могут потерпеть неудачу неожидаемым и непредсказуемым образом.

Примечание: текущая реализация имеет ошибку. В то время как коду Go разрешено записывать ноль или указатель C (но не указатель Go) в память C, текущая реализация может иногда вызывать ошибку времени выполнения, если содержимое памяти C выглядит как указатель Go. Поэтому избегайте передачи неинициализированной памяти C в код Go, если код Go будет хранить в ней значения указателя. Обнулите память в C, прежде чем передать ее в Go.


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


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

Функции Go могут быть экспортированы для использования кодом C следующим образом:

//export MyFunction
func MyFunction(arg1, arg2 int, arg3 string) int64 {...}

//export MyFunction2
func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...}

Они будут доступны в коде C как:

extern int64 MyFunction(int arg1, int arg2, GoString arg3);
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);

находятся в сгенерированном заголовке _cgo_export.h после копирования всех преамбул из входных файлов cgo. Функции с несколькими возвращаемыми значениями отображаются на функции, возвращающие структуру.

Не все типы Go могут быть сопоставлены с типами C полезным способом. Типы структур (struct) Go не поддерживаются; используйте тип структуры С. Типы массивов Go не поддерживаются; используйте указатель C.

Функции Go, которые принимают аргументы типа string, могут вызываться с типом C _GoString_, описанным в предыдущем посте. Тип _GoString_ будет автоматически определен в преамбуле. Обратите внимание, что в коде C нет способа создать значение этого типа; это полезно только для передачи строковых значений из Go в C и обратно в Go.

Использование //export в файле накладывает ограничение на преамбулу: поскольку он копируется в два разных выходных файла C, он не должен содержать никаких определений (definitions), только объявления (declarations). Если файл содержит определения и объявления, тогда два выходных файла будут производить дубликаты символов, и компоновщик потерпит неудачу. Чтобы избежать этого, определения должны быть помещены в преамбулы в другие файлы или в исходные файлы на языке C.


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


воскресенье, 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.


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


суббота, 13 июля 2019 г.

Команда cgo, использование cgo с командой go

Cgo позволяет создавать пакеты Go, которые вызывают C код.

Использование cgo с командой go

Чтобы использовать cgo, напишите обычный код Go, который импортирует псевдопакет "C". Затем код Go может ссылаться на типы, такие как C.size_t, переменные, такие как C.stdout, или функции, такие как C.putchar.

Если импорту "C" сразу предшествует комментарий, этот комментарий, называемый преамбулой, используется в качестве заголовка при компиляции частей C пакета. Например:

// #include <stdio.h>
// #include <errno.h>
import "C"

Преамбула может содержать любой код на C, включая объявления и определения функций и переменных. Затем они могут быть переданы из кода Go, как если бы они были определены в пакете "C". Могут использоваться все имена, объявленные в преамбуле, даже если они начинаются со строчной буквы. Исключение: на статические переменные в преамбуле нельзя ссылаться из кода Go; статические функции разрешены.

Смотрите примеры в $GOROOT/misc/cgo/stdio и $GOROOT/misc/cgo/gmp.

CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS и LDFLAGS могут быть определены с помощью псевдо-директив #cgo в этих комментариях для настройки поведения компилятора C, C++ или Fortran. Значения, определенные в нескольких директивах, объединяются вместе. Директива может включать список ограничений сборки, ограничивающих его действие системами, удовлетворяющими одному из ограничений. Например:

// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"

В качестве альтернативы, CPPFLAGS и LDFLAGS можно получить с помощью инструмента pkg-config, используя директиву #cgo pkg-config:, за которой следуют имена пакетов. Например:

// #cgo pkg-config: png cairo
// #include <png.h>
import "C"

Инструмент pkg-config по умолчанию можно изменить, установив переменную среды PKG_CONFIG.

По соображениям безопасности разрешен только ограниченный набор флагов, в частности -D, -I и -l. Чтобы разрешить дополнительные флаги, установите для CGO_CFLAGS_ALLOW регулярное выражение, совпадающее с новыми флагами. Чтобы запретить флаги, которые в противном случае были бы разрешены, установите для CGO_CFLAGS_DISALLOW регулярное выражение, совпадающее с аргументами, которые должны быть запрещены. В обоих случаях регулярное выражение должно соответствовать полному аргументу: чтобы разрешить -mfoo=bar, используйте CGO_CFLAGS_ALLOW='-mfoo.*', А не только CGO_CFLAGS_ALLOW='-mfoo'. Переменные с одинаковыми именами управляют разрешенными CPPFLAGS, CXXFLAGS, FFLAGS и LDFLAGS.

Также по соображениям безопасности разрешен только ограниченный набор символов, в частности буквенно-цифровые символы и несколько символов, таких как '.', которые не будут интерпретироваться неожиданным образом. Попытки использовать запрещенные символы приведут к ошибке "malformed #cgo argument".

При сборке переменные среды CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS и CGO_LDFLAGS добавляются к флагам, полученным из этих директив. Флаги, специфичные для пакета, должны быть установлены с использованием директив, а не переменных среды, чтобы сборки работали в неизмененных средах. Флаги, полученные из переменных среды, не подпадают под ограничения безопасности, описанные выше.

Все директивы cgo CPPFLAGS и CFLAGS в пакете объединяются и используются для компиляции файлов C в этом пакете. Все директивы CPPFLAGS и CXXFLAGS в пакете объединяются и используются для компиляции файлов C++ в этом пакете. Все директивы CPPFLAGS и FFLAGS в пакете объединяются и используются для компиляции файлов Fortran в этом пакете. Все директивы LDFLAGS в любом пакете программы объединяются и используются во время соединения (link time). Все директивы pkg-config объединяются и отправляются в pkg-config одновременно для добавления к каждому соответствующему набору флагов командной строки.

Когда директивы cgo анализируются, любое вхождение строки ${SRCDIR} будет заменено абсолютным путем к каталогу, содержащему исходный файл. Это позволяет предварительно скомпилированным статическим библиотекам быть включенными в каталог пакета и правильно связанными. Например, если пакет foo находится в каталоге /go/src/foo:

// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo

Будет расширен до:

// #cgo LDFLAGS: -L/go/src/foo/libs -lfoo

Когда инструмент Go видит, что один или несколько файлов Go используют специальный импорт "C", он ищет другие не-Go файлы в каталоге и компилирует их как часть пакета Go. Любые файлы .c, .s или .S будут скомпилированы с помощью компилятора C. Любые файлы .cc, .cpp или .cxx будут скомпилированы с помощью компилятора C++. Любые файлы .f, .F, .for или .f90 будут скомпилированы с помощью компилятора fortran. Любые файлы .h, .hh, .hpp или .hxx не будут скомпилированы отдельно, но, если эти файлы заголовков будут изменены, пакет (включая его исходные файлы не из Go) будет перекомпилирован. Обратите внимание, что изменения файлов в других каталогах не приводят к перекомпиляции пакета, поэтому весь не-Go исходный код для пакета должен храниться в каталоге пакета, а не в подкаталогах. Компиляторы C и C++ по умолчанию могут быть изменены переменными среды CC и CXX соответственно; эти переменные среды могут включать параметры командной строки.

Инструмент cgo включен по умолчанию для собственных сборок в системах, где он должен работать. По умолчанию это отключено при кросс-компиляции. Вы можете управлять этим, установив переменную окружения CGO_ENABLED при запуске инструмента go: установите его в 1, чтобы разрешить использование cgo, и в 0, чтобы отключить его. Инструмент go установит ограничение сборки "cgo", если включен cgo. Специальный импорт "C" подразумевает ограничение сборки "cgo", как будто файл также говорит "// +build cgo". Поэтому, если cgo отключен, файлы, импортирующие "C", не будут создаваться инструментом go.

При кросс-компиляции вы должны указать кросс-компилятор C для использования cgo. Вы можете сделать это, установив универсальную переменную среды CC_FOR_TARGET или более конкретную переменную среды CC_FOR_${GOOS}_${GOARCH} (например, CC_FOR_linux_arm) при построении цепочки инструментов с помощью make.bash, или вы можете установить переменную среды CC в любое время когда вы запускаете инструмент Go.

Переменные окружения CXX_FOR_TARGET, CXX_FOR_${GOOS}_${GOARCH} и CXX работают аналогичным образом для кода C++.


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


Команда cover в Golang

cover - это программа для анализа профилей покрытия, сгенерированных с помощью 'go test -coverprofile=cover.out'.

cover также используется 'go test -cover', чтобы переписать исходный код с аннотациями, чтобы отслеживать, какие части каждой функции выполняются. Он работает с одним исходным файлом Go за раз, вычисляя приблизительную базовую информацию блока, изучая источник. Таким образом, он более переносим, чем инструменты покрытия с бинарным переписыванием, но также немного менее способен. Например, он не проверяет внутри && и || выражений, и может быть слегка запутан единичными утверждениями с несколькими функциональными литералами.

При вычислении покрытия пакета, в котором используется cgo, инструмент вывода должен применяться к выходным данным предварительной обработки cgo, а не к входным данным, поскольку cover удаляет комментарии, важные для cgo.

Для информации об использовании, смотрите:

go help testflag
go tool cover -help

go tool cover

Использование go tool cover:

Учитывать профиль покрытия, созданный в 'go test':

go test -coverprofile=c.out


Открыть веб-браузер с аннотированным исходным кодом:

go tool cover -html=c.out


Записать файл HTML вместо запуска веб-браузера:

go tool cover -html=c.out -o coverage.html


Отобразить процент покрытия на стандартный вывод для каждой функции:

go tool cover -func=c.out


Наконец, для генерации модифицированного исходного кода с аннотациями покрытия (что делает go test -cover):

go tool cover -mode=set -var=CoverageVariableName program.go


Флаги:

-V 
  напечатать версию и выйти
-func string
  вывод информации о профиле покрытия для каждой функции
-html string
  генерировать HTML-представление профиля покрытия
-mode string
  режим покрытия: set, count, atomic
-o string
  файл для вывода; по умолчанию: stdout
-var string
  имя создаваемой переменной покрытия 
  (по умолчанию "GoCover")

Одновременно может быть установлен только один из -html, -func или -mode.


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


Функции тестирования в Golang

Команда 'go test' предполагает найти функции test, benchmark и example в файлах *_test.go, соответствующих тестируемому пакету.

Тестовая функция называется TestXxx (где Xxx не начинается со строчной буквы) и должна иметь сигнатуру,

func TestXxx(t *testing.T) { ... }

Контрольная функция называется BenchmarkXxx и должна иметь сигнатуру,

func BenchmarkXxx(b *testing.B) { ... }

Функция example похожа на функцию test, но вместо использования *testing.T для сообщения об успехе или неудаче выводит вывод в os.Stdout. Если последний комментарий в функции начинается с "Output:", то результат сравнивается точно с комментарием (см. Примеры ниже). Если последний комментарий начинается с "Unordered output:", то результат сравнивается с комментарием, однако порядок строк игнорируется. Пример без такого комментария компилируется, но не выполняется. Пример без текста после "Output:" компилируется, выполняется и, как ожидается, не будет производить вывод.

Godoc отображает тело ExampleXxx, чтобы продемонстрировать использование функции, константы или переменной Xxx. Пример метода M с типом получателя T или *T называется ExampleT_M. Может быть несколько примеров для данной функции, константы или переменной, отличающихся завершающим _xxx, где xxx - суффикс, не начинающийся с заглавной буквы.

Вот пример example:

func ExamplePrintln() {
    Println("The output of\nthis example.")
    // Output: The output of
    // this example.
}

Вот еще один пример, где порядок вывода игнорируется:

func ExamplePerm() {
    for _, value := range Perm(4) {
        fmt.Println(value)
    }

    // Unordered output: 4
    // 2
    // 1
    // 3
    // 0
}

Весь тестовый файл представлен в качестве example, когда он содержит одну example функцию, по крайней мере, одну другую функцию, тип, переменную или объявление константы, и не содержит test или benchmark функций.


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