2016-04-01 6 views
5

メインスレッドと破壊用のグローバル初期化コールを必要とするCライブラリ用のラッパー/ FFIを作成しています。ここでCライブラリの初期化/破壊ルーチンをラップするのに推奨される方法

は、私は現在、それを処理しています方法です:

のように使用することができ
struct App; 

impl App { 
    fn init() -> Self { 
     unsafe { ffi::InitializeMyCLib(); } 
     App 
    } 
} 

impl Drop for App { 
    fn drop(&mut self) { 
     unsafe { ffi::DestroyMyCLib(); } 
    } 
} 

:これは正常に動作しますが、それはハックのように感じ、生涯にこれらの呼び出しを結ぶ

fn main() { 
    let _init_ = App::init(); 
    // ... 
} 

不要な構造体のfinally(Java)またはat_exit(Ruby)ブロック内にデストラクタを持つことは、理論的にはより適切なようです。

ルストでこれを行うにはもっと優雅な方法がありますか?上記

代わりに私の第二のブロックのEDIT

lazy_staticクレートを使用して)そのように、この設定を使用しても安全/可能でしょう、:

lazy_static! { 
    static ref APP: App = App::new(); 
} 

は、この文献は保証されるだろう他のコードの前に初期化され、終了時に破棄されますか?ライブラリにlazy_staticを使用することは悪い習慣ですか?

これは、インスタンス化された構造体(元の例では_init_と呼ばれます)への参照を回避する必要がないため、この1つの構造体を介してFFIへのアクセスを容易にします。

これは、App構造体のデフォルトのコンストラクタをprivateにすることができるため、いくつかの点でより安全になります。

+1

サイドノート:あなたのラッパーの安全性に応じて、 'init'関数を安全にしないでください(誰も何度も呼び出さないかもしれないので)。そして、clibをメソッドとして初期化する必要があるすべての関数を実装してください'App'オブジェクトに追加します。このようにして、誰も関数を初期化せずに呼び出すことはできません。初期化するのを安全にするために、何らかのリファレンスカウントシングルトンとして実装することもできます。 JavaとRubyよりも大きな勝利です。なぜなら、メインスレッドでlib –

+0

*を初期化せずに関数を呼び出すことができるからです。*あなたは**本当に**メインスレッドでなければなりませんか?使用前に初期化されている限り、どのスレッドでもかまいませんか? – Shepmaster

+0

@ker、コメントありがとうございます。私はそのようなことは考えていませんでした。( 'App'構造体を通してすべてのアクセスを持っていますが、それは意味があります。)私のケースでこれがうまくいくかどうか考える必要があります。 –

答えて

2

私は強く言い渡されたドキュメントを超えてメインスレッドでメソッドを呼び出すことを強制する方法がないことを知っています。

1回を実行するために使用することができます同期プリミティブ:だから、その要件を無視して... :-)

一般的に、私は基本的にこのような場合のために設計されているようだstd::sync::Onceを、使用したいですグローバル 初期化。 FFIまたは関連する 機能の1回限りの初期化に役立ちます。このタイプはONCE_INIT 値でのみ構築できます。

クリーンアップの規定はありません。ライブラリが何をしていたとしても、何度もリークしなければなりません。通常、ライブラリに専用のクリーンアップパスがある場合、初期化されたすべてのデータをある種のコンテキストまたは環境として後続の関数に渡される型に格納するように構造化されています。これはRust型にうまく対応します。あなたはそれがあると思いますよう

警告あなたの現在のコードは、保護としてないです。あなたのAppが空の構造体であるため、エンドユーザーは、あなたの方法を呼び出すことなく、それを構築することができます。

let _init_ = App; 

私は現時点では詳細で適切な質問を見つけることはできませんが、私はPhantomDataを使用したいですあなたのモジュールの外部からより多くのものを保護してください。

要するに、私はこのようなものを使用したい:

use std::sync::{Once, ONCE_INIT}; 
use std::marker::PhantomData; 

mod ffi { 
    extern { 
     pub fn InitializeMyCLib(); 
     pub fn CoolMethod(arg: u8); 
    } 
} 

static C_LIB_INITIALIZED: Once = ONCE_INIT; 

#[derive(Copy, Clone)] 
struct TheLibrary { 
    marker: PhantomData<()>, 
} 

impl TheLibrary { 
    fn new() -> Self { 
     C_LIB_INITIALIZED.call_once(|| { 
      unsafe { 
       ffi::InitializeMyCLib(); 
      } 
     }); 
     TheLibrary { 
      marker: PhantomData, 
     } 
    } 

    fn cool_method(&self, arg: u8) { 
     unsafe { ffi::CoolMethod(arg) } 
    } 
} 

fn main() { 
    let lib = TheLibrary::new(); 
    lib.cool_method(42); 
} 
+0

お返事ありがとうございます!あなたの 'Once'の使用は私に' lazy_static'の箱を思い出させました。私はこれを扱う別の方法で質問を更新しましたが、私はそうしません。それが音であるかどうかを知る。 –

1

をIやったいくつかの他のFFIのLIBSには、この状況に対処方法を確認するために周りに掘り。

fn initialize() { 
    static INIT: Once = ONCE_INIT; 
    INIT.call_once(|| unsafe { 
     ffi::InitializeMyCLib(); 
     assert_eq!(libc::atexit(cleanup), 0); 
    }); 

    extern fn cleanup() { 
     unsafe { ffi::DestroyMyCLib(); } 
    } 
} 

私はその後、私の公共の構造体のためにpublicコンストラクタ内で、この関数を呼び出します。ここで私は現在、(Shepmasterの答え@に類似してcurl-rustの初期化ルーチンに緩く基づく)を使用していますものです。

関連する問題