2016-04-26 5 views
0

私はGoライブラリ(gofileseq)を所有しています。そのためにC/C++バインディングを試してみたいと思います。複雑なGo構造体をCにエクスポート/ラップすることはできますか?

単純な型(int、文字列など)を使用する関数をエクスポートするのはかなり簡単です。 Cの構造体を定義し、Goの型をそれに変換することで、エクスポートされた関数で使用するためにCのメモリを割り当てるので、カスタムのGo型からCにデータをエクスポートするだけでも簡単です。しかし、go 1.5 cgo rulesでは、状態を格納するより複雑な構造体から機能をエクスポートする方法を理解するのは難しいと感じています。私はC++バインディングに何とかエクスポートしたいgofileseqから構造体の

例:

// 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ルールは、関数呼び出しよりもGoポインタを格納していないCを許可しません。そして私はGoライブラリを使って動作するために割り当てられ使用されるC++クラスを定義するアプローチをどのように使うことができないのか見当ろうとしていません。

C/C++へのエクスポージャーのために複合型をラップすることはできますか? C++クライアントがGo FrameSetを作成するためのパターンがありますか?

編集私はC++が静的map[int]*FrameSetに行く側に保存されて取りに行くにオブジェクトを作成し、C++にint型のIDを返すようにするだろうと考えることができ

ひとつのアイデア。その後、すべてのC++演算がidにGoを要求します。それは有効な解決策のように聞こえるか?

更新

今のところ、私は、オブジェクトを格納するグローバルマップと固有のIDを使用してソリューションのテストを進めています。 C++は新しいオブジェクトの作成を要求し、不透明なIDのみを取得します。そして、そのIDを使用して、関数としてエクスポートされたすべてのメソッドを呼び出すことができます(完了時に破棄することを要求するなど)。

これよりも優れたアプローチがある場合は、私は答えを見たいと思います。完全に動作するプロトタイプを取得したら、私自身の答えを追加します。

アップデート#2

私は私が使用して終了し、最終的な解決策についてのブログ記事を書いた:http://justinfx.com/2016/05/14/cpp-bindings-for-go/

答えて

0

私はこれを解決することになった道を、よりよい解決策の欠如のために、 Go側(ref)のプライベートグローバルマップを使用することでした。これらのマップはGoオブジェクトのインスタンスをランダムなuint64 idに関連付け、idは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++の相互作用が不透明なハンドルを介して通信します:

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

残念ながらそれはmulthreaded C++環境では、すべてのスレッドがマップにミューテックスを通過することを意味しています。しかし、オブジェクトが作成され、破棄されたときの書き込みロックのみであり、オブジェクトに対するすべてのメソッド呼び出しでは、読み取りロックです。

関連する問題