4

ブロックを使わずに、特性オブジェクトをゼロから作成してみることができると思いました。impl詳しく説明するには:

trait SomeTrait {
    fn fn_1(&self);
    fn fn_2(&self, a: i64);
    fn fn_3(&self, a: i64, b: i64);
}

struct TraitObject {
    data: *mut (),
    vtable: *mut (),
}

fn dtor(this: *mut ()) {
    // ...
}

fn imp_1(this: *mut ()) {
    // ...
}

fn imp_2(this: *mut (), a: i64) {
    // ...
}

fn imp_3(this: *mut (), a: i64, b: i64) {
    // ...
}

fn main() {
    let data = &... as *mut (); // something to be the object
    let vtable = [dtor as *mut (),
                  8 as *mut (),
                  8 as *mut (),
                  imp_1 as *mut (),
                  imp_2 as *mut (),
                  imp_3 as *mut ()]; // ignore any errors in typecasting,
        //this is not what I am worried about getting right

    let to = TraitObject {
        data: data,
        vtable: vtable.as_ptr() as *mut (),
    };
    // again, ignore any typecast errors,

    let obj: &SomeTrait = unsafe { mem::transmute(to) };

    // ...

    obj.fn_1();
    obj.fn_2(123);
    obj.fn_3(123, 456);
}

私が理解していることから、メンバー関数が特性定義に現れる順序は、関数ポインターが VTable に現れる順序と必ずしも同じではありません。VTable 内の各特性メソッドのオフセットを決定する方法はありますか?

4

1 に答える 1

3

実行時にレイアウトを検出してもかまわない場合は、特定のオフセットで関数のアドレスを比較し、それらを既知のダミー実装のアドレスと比較して一致させることができます。これは、すべてのメソッドを読み取る必要がある場合があるため、トレイトに含まれるメソッドの数を知っていることを前提としています。

use std::mem;

trait SomeTrait {
    fn fn_1(&self);
    fn fn_2(&self, a: i64);
    fn fn_3(&self, a: i64, b: i64);
}

struct Dummy;

impl SomeTrait for Dummy {
    fn fn_1(&self) { unimplemented!() }
    fn fn_2(&self, _a: i64) { unimplemented!() }
    fn fn_3(&self, _a: i64, _b: i64) { unimplemented!() }
}

struct TraitObject {
    data: *mut (),
    vtable: *mut (),
}

fn main() {
    unsafe {
        let fn_1 = Dummy::fn_1 as *const ();
        let fn_2 = Dummy::fn_2 as *const ();
        let fn_3 = Dummy::fn_3 as *const ();

        let dummy = &mut Dummy as &mut SomeTrait;
        let dummy: TraitObject = mem::transmute(dummy);
        let vtable = dummy.vtable as *const *const ();
        let vtable_0 = *vtable.offset(3);
        let vtable_1 = *vtable.offset(4);
        let vtable_2 = *vtable.offset(5);

        // Mapping vtable offsets to methods is left as an exercise to the reader. ;)
        println!("{:p} {:p} {:p}", fn_1, fn_2, fn_3);
        println!("{:p} {:p} {:p}", vtable_0, vtable_1, vtable_2);
    }
}
于 2016-09-27T00:33:18.073 に答える