You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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代码完全错误,原因有两点:

  1. C++成员函数隐含this指针:不管是普通成员函数还是虚函数,第一个参数都是指向对象实例的this指针,你直接声明成仅接收a和b两个参数,调用时根本没有传递对象实例,逻辑不成立。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 00:35:19