2017-12-23 13 views
2

私はepoll Linux APIのラッパーを作成しようとしています。 thisリポジトリをフォークしましたが、epoll APIで使用されているunionタイプは使用しません。私はRustのCユニオン機能を使用して、安全でないコードを使用する必要のない完全なラッパーを作成することにしました。組合に強制的に1つのタイプのように振る舞う方法はありますか?

このユニオンは私にいくつかの問題を引き起こします。

コンパイル時に使用された共用体の型を1つの型にロックするにはどうすればよいですか? epollの組合は区別できません。あなたはepoll fdによって1つの組合員しか使えません。まあ、それは安全ですが、それはできません。

ユーザーは、複合型のフィールドのptrフィールドとして列挙型を使用できますが、これはRustのenumを使用するため安全です。

「generic」または「macro」で検索しましたが、私の意志に合う方法は見つかりません。

fn main() { 
    let event = event!(ptr, Box::new(42)); 
    let _ = event.ptr(); 
    let _ = event.fd(); // fails to compile; we can only use ptr 
} 

マイフォークはhereを見つけることができます:

extern crate libc; 

#[derive(Clone, Copy)] 
pub union Data { 
    pub ptr: *mut libc::c_void, 
    pub fd: std::os::unix::io::RawFd, 
    pub u32: libc::uint32_t, 
    pub u64: libc::uint64_t, 
} 

#[repr(C)] 
#[repr(packed)] 
#[derive(Clone, Copy)] 
pub struct Event { 
    pub data: Data, 
} 

impl Event { 
    fn new(data: Data) -> Event { 
     Event { data: data } 
    } 
} 

fn main() { 
    let event = Event::new(Data { 
     ptr: Box::into_raw(Box::new(42)) as *mut libc::c_void, 
    }); 
    unsafe { Box::from_raw(event.data.ptr) }; 
} 

私はそのような何かをしたいと思います。マクロ、ジェネリック、その他が適切な解決策であるかどうかわかりません。あなたは私のコード、特にthe integration testを見ることができます、安全でないコードがたくさんあります、私はユーザー側から安全でないコードを可能な限り削除したいと思います。現在は非常に醜いです。

答えて

3

別の種類のユニオンをラップすることができます。そのタイプは特定の特性のジェネリックパラメータを持ちます。具体的な実装に基づいて、特定のメソッドセットを実装できます。

この場合、組合DataをタイプEventにラップします。 Eventには、EventModeを実装するすべてのタイプの汎用タイプパラメータがあります。私たちは具体的な種類Event<Fd>Event<Ptr>のための特定のメソッドを実装します。event.fd()を呼び出そうと

extern crate libc; 
use std::os::unix::io::RawFd; 
use std::marker::PhantomData; 

#[derive(Copy, Clone)] 
pub union Data { 
    pub ptr: *mut libc::c_void, 
    pub fd: RawFd, 
} 

trait EventMode {} 

#[derive(Debug, Copy, Clone)] 
struct Fd { 
    _marker: PhantomData<RawFd>, 
} 
impl Fd { 
    fn new() -> Self { 
     Fd { 
      _marker: PhantomData, 
     } 
    } 
} 
impl EventMode for Fd {} 

#[derive(Debug, Copy, Clone)] 
struct Ptr<T> { 
    _marker: PhantomData<Box<T>>, 
} 
impl<T> Ptr<T> { 
    fn new() -> Self { 
     Ptr { 
      _marker: PhantomData, 
     } 
    } 
} 
impl<T> EventMode for Ptr<T> {} 

#[derive(Copy, Clone)] 
pub struct Event<M> { 
    pub data: Data, 
    mode: M, 
} 

impl Event<Fd> { 
    fn new_fd(fd: RawFd) -> Self { 
     Event { 
      data: Data { fd }, 
      mode: Fd::new(), 
     } 
    } 

    fn fd(&self) -> RawFd { 
     unsafe { self.data.fd } 
    } 
} 

impl<T> Event<Ptr<T>> { 
    fn new_ptr(t: T) -> Self { 
    let ptr = Box::into_raw(Box::new(t)) as *mut _; 
     Event { 
      data: Data { 
       ptr: ptr, 
      }, 
      mode: Ptr::new(), 
     } 
    } 

    fn ptr(&self) -> &T { 
     unsafe { &*(self.data.ptr as *const T) } 
    } 
} 

fn main() { 
    let event = Event::new_ptr(42); 
    println!("{}", event.ptr()); 
    // event.fd(); 
} 

はエラーを与える:ボーナスとして

error[E0599]: no method named `fd` found for type `Event<Ptr<{integer}>>` in the current scope 
    --> src/main.rs:76:11 
    | 
76 |  event.fd(); 
    |   ^^ 

、私たちは何を具体的な形をエンコードするために同じPhantomDataを使用することができます私たちが組合に隠していたポインタの値は、それを取得するために行くときにそれが不一致であることを避けます。

厳密に言えば、この特性はには必要ありません。がありますが、それは素晴らしい組み込みのドキュメントを提供していると思います。

も参照してください:

+0

私はそれは私がしたい正確に何だと思います。 – Stargateur

+0

少し複雑です。なぜ、あなたは 'struct Ptr(Data)'と 'struct Fd(Data)'を持っていて、共通の振る舞いを特性に入れませんか?私はこれがどのように使われているのかは不明だし、何かが足りないかもしれない。 – trentcl

+0

@trentcl複数の回答がありえない理由はありません:-) – Shepmaster

関連する問題