Структуры в Си и CGO
По горячим следам хочу написать какая забытая мною особенность языка Си вчера ввела меня в заблуждение и почему несмотря на то, что я работал с сишной структурой, CGO так как будто не считало.
Для начала посмотрим как определяются и передаются структуры в Си. Полную спецификацию я приводить не буду, посмотрим на два распространённых способа. Первый используется в этом небольшом примере:
#include <stdio.h>
#include <math.h>
struct point {
double x, y;
};
double point(struct point p) {
return sqrt(pow(p.x, 2) + pow(p.y, 2));
}
int main() {
printf("%g\n", point((struct point){1, 2}));
}
Тут можно обратить внимание на две особенности — во-первых, везде, где мы используем структуру, приходится использовать и ключевое слово struct, во-вторых, в коде существует и структура point, и функция с таким названием.
Это происходит потому, что имена структур обитают в своём собственном пространстве имён, на его использование и указывает struct. Там же живут имена объединений (union) и перечислений (enum). Имена функций находятся в общем пространстве, так что два таких определения с одним именем друг другу не мешают.
Структуры, объявленные таким образом видны через CGO компилятора Гоу как C.struct_name (C.struct_point в данном случае). К сожалению, тут есть особенность, о которую я споткнулся — если запрашиваемая структура не определена, то Гоу не выдаст ошибки, мы просто получим пустую структуру:
package main
import ("C"; . "fmt")
func main() {
Println(C.struct_undefined{}) // ошибки не будет
}
Но программисты — люди ленивые, писать всюду struct неудобно. К счастью есть способ этого не делать — можно завести структуру, а потом дать ей имя в пространстве всех остальных типов. Это делается при помощи ключевого слова typedef позволяющего создавать алиасы типов:
struct point {
double x, y;
};
typedef struct point point;
Теперь у нас есть два имени для одного типа — один в пространстве структур и прочего, второе — в общем пространстве. Можно даже сделать их одинаковыми, что очень удобно. Если второе имя нам ни к чему (бывают случаи, когда оно необходимо, но я не буду их тут рассматривать), то можно определить анонимную структуру и сразу дать имя этому типу в общем пространстве, так тоже будет работать.
Это нас подводит ко второму способу работы со структурами:
#include <stdio.h>
#include <math.h>
typedef struct {
double x, y;
} point;
double veclen(point p) {
return sqrt(pow(p.x, 2) + pow(p.y, 2));
}
int main() {
printf("%g\n", veclen((point){1, 2}));
}
Ключевое слово struct перед типом уже не нужно, раз все имена в общем пространстве. Это же касается и CGO.
Как раз на эти грабли я и наступил — позабыл про эти все особенности. Когда я ещё писал на Си, всегда пользовался вторым способом, с именем в общем пространстве и привык к тому, что это и есть структуры.
Что-то это уже не работает.. Если создавать структуру через альяс — компилятор выдаёт ошибку..(could not determine kind of name for C.name) Golang (v1.11). Возможно пофиксили.
Надо же, я думал, что они в CGO уже ничего не меняют.