Можно ли написать функции C, которые изменяют структуры типов, определенных в коде Go?

Это продолжение этого вопроса. Я сделал предположение, что это может быть неверно, поэтому я прямо спрашиваю об этом. Поскольку я забыл спросить, действительно ли это возможно, я уже зарегистрировал issue # 8114 об этом.


С помощью cgo возможно, что код Go работает на типах C, например:

package foo

//#include <sys/stat.h>
import "C"

func fileSizeFromStat(stat *C.struct_stat) int64 {
    return int64(stat.st_size)
}

Возможно ли обратное? То есть записывать функции C, которые работают с типами go? Конкретная точка этого изложена в связанном выше вопросе; Я хочу, чтобы marshall C-структуры, к которым невозможно получить доступ из кода Go, либо потому, что они используют союзы или битовые поля, либо потому, что их выравнивание делает их несовместимыми с кодом Go.

Ответ 1

Насколько я знаю, нет, вы не можете.

Но вы могли бы использовать что-то некрасивое, как https://github.com/OneOfOne/go-nfqueue/blob/master/nfqueue.go#L130, где вы экспортируете функцию Go, которая занимает много указателей и создает вашу структуру Go в перейти.

Ответ 2

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

Для каждой структуры Go я хочу получить доступ к

type JewelTarget struct {
    SensRes [2]byte
    Id      [4]byte
    Baud    int
}

Я создаю соответствующую структуру C, которая имеет поля с одинаковой шириной и, надеюсь, один и тот же макет:

typedef ptrdiff_t GoInt;

struct JewelTarget {
    uint8_t SensRes[2];
    uint8_t Id[4];
    GoInt   Baud;
};

Затем я пишу C-функции, которые используют эти C-структуры:

extern void
marshallJewelTarget(nfc_target *nt, const struct JewelTarget *jt)
{
    nfc_jewel_info *ji = &nt->nti.nji;

    memcpy(ji->btSensRes, jt->SensRes, sizeof(jt->SensRes));
    memcpy(ji->btId, jt->Id, sizeof(jt->Id));

    nt->nm.nbr = jt->Baud;
    nt->nm.nmt = NMT_JEWEL;
}

и назовите их так, как если бы у аргументов были соответствующие типы Go:

func (d *JewelTarget) Marshall() uintptr {
    nt := mallocTarget()
    jt := (*C.struct_JewelTarget)(unsafe.Pointer(d))

    C.marshallJewelTarget(nt, jt)

    return uintptr(unsafe.Pointer(nt))
}

Все примеры, взятые из привязок nfc.