2016-09-26 89 views
4

我以爲我可以嘗試或多或少從零開始構建特徵對象,而不使用impl塊。爲了詳細說明:有沒有辦法確定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); 
} 

據我瞭解,在成員函數出現在特徵定義的順序並不總是一樣的指針出現在虛函數表的功能。有沒有辦法確定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

這是我最初嘗試使用的方法。你知道是否可以通過名稱來確定函數的偏移嗎?所以我理論上可以在運行時覆蓋函數?即「覆蓋(對象, :: fn_1,impl_1)'? –

+0

我不認爲這是可能的。在C++中,我認爲你可以用成員函數指針來做這件事,但Rust沒有這些。 –

+0

該死的。那麼我想我至少可以看到我可以用TraitObjects和Dynamic Dispatch推動這個界限。不過這是一個很好的開始。 –

相關問題