2016-09-26 3 views
4

Я думал, что смогу попробовать более или менее построить объект с нуля без использования блоков impl. Выработать:Есть ли способ определить смещения каждого из методов trait в VTable?

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); 
} 

Из того, что я понимаю, порядок, в котором функция членов появляется в определении признака не всегда совпадает с функцией указатели появляются в виртуальных таблицах. Есть ли способ определить смещения каждого из методов trait в VTable?

ответ

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); 
    } 
} 
+0

Это подход, который я изначально попытался использовать. Знаете ли вы, можно ли определить смещения функций по имени? Поэтому я мог теоретически переопределить функции во время выполнения? то есть 'override (object, :: fn_1, impl_1)'? –

+0

Я не думаю, что это возможно. В C++ я думаю, что вы можете сделать это с помощью указателей на функции, но у Rust нет таких. –

+0

Черт. Ну, я думал, что, по крайней мере, буду видеть, как далеко я могу продвигать границы с TraitObjects и Dynamic Dispatch. Это довольно неплохое начало. –