Использование кода Go в существующем проекте C

С тех пор, как вышел Go 1.5, я начал еще раз взглянуть на то, как я могу интегрировать его в существующий проект.

База кода проекта написана полностью на языке C для доступа к аппаратным средствам и другим забавным материалам на низком уровне. Однако некоторые вещи более высокого уровня утомительны, и я хотел бы начать писать их на языке более высокого уровня (Go)

Можно ли каким-либо образом вызвать код Go из программы C? Я установил Go 1.5, который добавил -buildmode=c-archive (https://golang.org/s/execmodes), который я пытаюсь получить.

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

Я новичок в Go - однако, я люблю этот язык и хотел бы использовать его. Есть ли какой-либо идиоматический способ ( "идиоматический" часто используется в мире Go, который я вижу...), чтобы заставить это играть красиво друг с другом?

Причина, по которой я задал этот вопрос и конкретно упомянул Go 1.5, заключается в том, что согласно этому документу https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit?pli=1#heading=h.1gw5ytjfcoke Go 1.5 добавлена ​​поддержка программ non-Go для вызова кода Go. В частности, упоминается в разделе "Код Go, связанный с программой non-Go и вызываемый из нее"

Ответ 1

Чтобы создать архив, вызываемый с C, вам нужно будет пометить их как экспортированные символы CGo.
Например, если я создаю файл foo.go со следующим содержимым:

package main

import (
    "C"
    "fmt"
)

//export PrintInt
func PrintInt(x int) {
    fmt.Println(x)
}

func main() {}

Важно отметить:

  • Пакет нужно называть main
  • Вам нужно иметь функцию main, хотя она может быть пустой.
  • Вам нужно импортировать пакет C
  • Вам нужны специальные //export комментарии для обозначения функций, которые вы хотите вызывать из C.

Я могу скомпилировать его как статическую библиотеку C с возможностью вызова следующей команды:

go build -buildmode=c-archive foo.go

Результатом будет архив foo.a и заголовок foo.h. В заголовке мы получаем следующее (возвращающее нерелевантные части):

...
typedef long long GoInt64;
...
typedef GoInt64 GoInt;
...
extern void PrintInt(GoInt p0);
...

Таким образом, чтобы вызвать экспортированную функцию. Мы можем написать простую программу на языке C, которая вызывает ее так:

#include "foo.h"

int main(int argc, char **argv) {
    PrintInt(42);
    return 0;
}

Мы можем скомпилировать его командой:

gcc -pthread foo.c foo.a -o foo

Опция -pthread необходима, потому что во время выполнения Go использует потоки. Когда я запускаю результирующий исполняемый файл, он печатает 42.