如何从Rust调用C++方法?虚表(VMT)实现及错误代码求助
Rust调用C++方法:虚表(VMT)使用与当前实现的区别
问题
想从Rust中调用C++的成员方法,听说需要用到虚表(VMTs),不清楚具体操作方式,也想知道这和自己当前的实现有什么区别。
提供的C++代码:
struct numbers { int addnums(int a, int b) { return a + b; } }; struct v_numbers { virtual int v_addnums(int a, int b) { return a + b; } };
当前无法链接的Rust代码:
extern "system" { fn addnums(a: i32, b: i32) -> i32; fn v_addnums(a: i32, b: i32) -> i32; } fn main() { unsafe { println!("{}", addnums(1, 2)); println!("{}", v_addnums(1, 2)); } }
当前实现的问题
你写的Rust代码完全错误,原因有两点:
- C++成员函数隐含this指针:不管是普通成员函数还是虚函数,第一个参数都是指向对象实例的
this指针,你直接声明成仅接收a和b两个参数,调用时根本没有传递对象实例,逻辑不成立。 - C++函数名修饰:默认情况下,C++编译器会对函数名进行修饰(比如把
numbers::addnums变成_ZN7numbers7addnumsEii),Rust中直接用addnums找不到对应的符号,导致链接失败。
虚表(VMT)的作用与调用方式
虚表是C++实现多态的底层机制:当类包含虚函数时,每个对象实例的内存布局开头会有一个指针,指向该类的虚函数表(VMT),表中存储了所有虚函数的地址。调用虚函数时,程序会先通过对象指针获取虚表指针,再找到对应函数地址,最后传递this指针和参数执行。
你不需要手动创建虚表,编译器会自动生成。调用虚函数有两种方式:
方式1:用extern "C"封装C++接口(推荐)
这是最稳妥、可移植的方式,通过C风格的包装函数隐藏C++的细节,Rust直接调用这些包装函数即可。
修改后的C++代码:
#include <cstdlib> struct numbers { int addnums(int a, int b) { return a + b; } }; struct v_numbers { virtual int v_addnums(int a, int b) { return a + b; } }; // 用extern "C"导出C风格接口,避免名字修饰 extern "C" { // 普通成员函数相关 numbers* create_numbers() { return new numbers(); } int call_addnums(numbers* obj, int a, int b) { return obj->addnums(a, b); } void destroy_numbers(numbers* obj) { delete obj; } // 虚函数相关 v_numbers* create_v_numbers() { return new v_numbers(); } int call_v_addnums(v_numbers* obj, int a, int b) { return obj->v_addnums(a, b); } void destroy_v_numbers(v_numbers* obj) { delete obj; } }
对应的Rust代码:
extern "C" { // 普通成员函数接口 fn create_numbers() -> *mut (); fn call_addnums(obj: *mut (), a: i32, b: i32) -> i32; fn destroy_numbers(obj: *mut ()); // 虚函数接口 fn create_v_numbers() -> *mut (); fn call_v_addnums(obj: *mut (), a: i32, b: i32) -> i32; fn destroy_v_numbers(obj: *mut ()); } fn main() { unsafe { // 调用普通成员函数 let num_obj = create_numbers(); println!("普通成员函数结果: {}", call_addnums(num_obj, 1, 2)); destroy_numbers(num_obj); // 调用虚函数 let v_num_obj = create_v_numbers(); println!("虚函数结果: {}", call_v_addnums(v_num_obj, 3, 4)); destroy_v_numbers(v_num_obj); } }
方式2:直接操作虚表(不推荐)
这种方式依赖C++编译器的内存布局,不可移植,仅作演示:
假设v_numbers对象的内存布局是[虚表指针],Rust中可以手动解析虚表并调用:
// 内存布局需与C++编译器一致,不同环境可能变化 #[repr(C)] struct VTable { v_addnums: unsafe extern "C" fn(*mut (), i32, i32) -> i32, } #[repr(C)] struct VNumbers { vtable: *const VTable, } extern "C" { fn create_v_numbers() -> *mut VNumbers; fn destroy_v_numbers(obj: *mut VNumbers); } fn main() { unsafe { let obj = create_v_numbers(); let vtable = (*obj).vtable; let result = ((*vtable).v_addnums)(obj as *mut (), 5, 6); println!("直接通过虚表调用结果: {}", result); destroy_v_numbers(obj); } }
总结
- 你当前的实现错误在于忽略了C++成员函数的
this指针和名字修饰问题,导致链接失败。 - 虚表是C++多态的底层实现,无需手动创建,推荐通过
extern "C"封装接口的方式调用,避免直接操作虚表带来的兼容性问题。
内容的提问来源于stack exchange,提问作者whatisdisplayname
相关产品推荐
相关产品推荐

