Можно ли экспортировать / обернуть сложную структуру Go в C?

У меня есть библиотека Go, gofileseq, для которого я хотел бы попытаться сделать привязку C / C ++.

Это довольно просто, чтобы иметь возможность экспортировать функции, которые используют простые типы (целые числа, строки, …). Даже достаточно просто экспортировать данные из пользовательских типов Go в C, определив структуру C и переведя в нее тип Go, который будет использоваться в экспортируемых функциях, поскольку вы выделяете память C для этого. Но с перейти на 1,5 сго правила Мне трудно понять, как экспортировать функциональность из более сложной структуры, в которой хранится состояние.

Пример структуры из gofileseq, которую я хотел бы как-то экспортировать в привязку C ++:

// package fileseq
//

type FrameSet struct {
frange   string
rangePtr *ranges.InclusiveRanges
}

func NewFrameSet(frange string) (*FrameSet, error) {
// bunch of processing to set up internal state
}

func (s *FrameSet) Len() int {
return s.rangePtr.Len()
}

// package ranges
//

type InclusiveRanges struct {
blocks []*InclusiveRange
}

type InclusiveRange struct {
start int
end   int
step  int

cachedEnd   int
isEndCached bool

cachedLen   int
isLenCached bool
}

Как видите, FrameSet Тип, который я хочу предоставить, содержит часть указателей на базовый тип, каждый из которых хранит состояние.

В идеале я хотел бы иметь возможность хранить void* в классе C ++ и сделать его простым прокси для обратного вызова экспортированных функций Go с void*, Но правила cgo запрещают C хранить указатель Go дольше, чем вызов функции. И я не вижу, как я мог бы использовать подход определения классов C ++, которые можно было бы распределять и использовать для работы с моей библиотекой Go.

Можно ли обернуть сложные типы для воздействия на C / C ++?
Есть ли шаблон, который позволил бы клиенту C ++ создавать Go FrameSet?

редактировать

Одна идея, о которой я могу подумать, — позволить C ++ создавать объекты в Go, которые хранятся на стороне Go в статическом виде. map[int]*FrameSet а затем вернуть идентификатор int в C ++. Затем все операции C ++ делают запросы в Go с идентификатором. Это похоже на правильное решение?

Обновить

Сейчас я продолжаю тестировать решение, которое использует глобальные карты и уникальные идентификаторы для хранения объектов. C ++ будет запрашивать создание нового объекта и получать только непрозрачный идентификатор. Затем они могут вызывать все методы, экспортируемые как функции, используя этот идентификатор, в том числе запрашивая его уничтожение по завершении.

Если есть лучший подход, чем это, я хотел бы увидеть ответ. Как только я получу полностью работающий прототип, я добавлю свой ответ.

Обновление № 2

Я написал сообщение в блоге об окончательном решении, которое я использовал в итоге: http://justinfx.com/2016/05/14/cpp-bindings-for-go/

0

Решение

Я решил, что из-за отсутствия лучшего решения я решил использовать частные глобальные карты на стороне Go (ссылка). Эти карты будут ассоциировать экземпляры объектов Go со случайным идентификатором uint64, и идентификатор будет возвращен в C ++ как «непрозрачный дескриптор».

type frameSetMap struct {
lock *sync.RWMutex
m    map[FrameSetId]*frameSetRef
rand idMaker
}
//...
func (m *frameSetMap) Add(fset fileseq.FrameSet) FrameSetId {
// fmt.Printf("frameset Add %v as %v\n", fset.String(), id)
m.lock.Lock()
id := FrameSetId(m.rand.Uint64())
m.m[id] = &frameSetRef{fset, 1}
m.lock.Unlock()
return id
}

Затем я использую подсчет ссылок, чтобы определить, когда C ++ больше не нуждается в объекте, и удаляю его из карты:

// Go
func (m *frameSetMap) Incref(id FrameSetId) {
m.lock.RLock()
ref, ok := m.m[id]
m.lock.RUnlock()

if !ok {
return
}

atomic.AddUint32(&ref.refs, 1)
// fmt.Printf("Incref %v to %d\n", ref, refs)
}

func (m *frameSetMap) Decref(id FrameSetId) {
m.lock.RLock()
ref, ok := m.m[id]
m.lock.RUnlock()

if !ok {
return
}

refs := atomic.AddUint32(&ref.refs, ^uint32(0))
// fmt.Printf("Decref %v to %d\n", ref, refs)
if refs != 0 {
return
}

m.lock.Lock()
if atomic.LoadUint32(&ref.refs) == 0 {
// fmt.Printf("Deleting %v\n", ref)
delete(m.m, id)
}
m.lock.Unlock()
}

//C++
FileSequence::~FileSequence() {
if (m_valid) {
//        std::cout << "FileSequence destroy " << m_id << std::endl;
m_valid = false;
internal::FileSequence_Decref(m_id);
m_id = 0;
m_fsetId = 0;
}
}

И все взаимодействия C ++ с экспортированной библиотекой Go связываются через непрозрачный дескриптор:

// C++
size_t FileSequence::length() const {
return internal::FileSequence_Len(m_id);
}

К сожалению, это означает, что в многопоточной среде C ++ все потоки проходят через мьютекс на карту. Но это только блокировка записи, когда объекты создаются и уничтожаются, а для всех вызовов методов объекта это блокировка чтения.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]