rust - 如何调用返回多种类型的不同函数指针的 C 函数?

标签 rust ffi

我正在尝试访问 OpenGL 函数 glCreateVertexArrays。考虑以下小型 C++ 程序:

#include <GL/glx.h>
#include <GLFW/glfw3.h>

int main() {
    // Init GLFW
    glfwInit();

    // Set OpenGL Version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

    // Select core profile
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);

    // Create Window
    auto window = glfwCreateWindow(400, 400, "Test", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    void (*glCreateVertexArrays)(unsigned int, unsigned int*) = nullptr;
    glCreateVertexArrays = (decltype(glCreateVertexArrays))glXGetProcAddress((const       GLubyte*)"glCreateVertexArrays");

    unsigned int vao;
    glCreateVertexArrays(1, &vao); // <-- This is the important part

    return 0;
}

这个应用程序没有段错误;现在我想在 Rust 中重新创建相同的内容:

extern crate libc;

use std::ffi::CString;
use std::os;
use std::ptr;

enum GLFWwindow {}
enum GLFWmonitor {}
#[link(name = "glfw")]
extern "C" {
    fn glfwInit() -> bool;
    fn glfwWindowHint(target: libc::uint32_t, hint: libc::int32_t);
    fn glfwCreateWindow(
        width: libc::c_int,
        height: libc::c_int,
        title: *const os::raw::c_char,
        monitor: *mut GLFWmonitor,
        window: *mut GLFWwindow,
    ) -> *mut GLFWwindow;
    fn glfwMakeContextCurrent(window: *mut GLFWwindow);
}

#[link(name = "GL")]
extern "C" {
    fn glXGetProcAddress(procname: *const os::raw::c_char) -> extern "C" fn();
}

static GLFW_CONTEXT_VERSION_MAJOR: u32 = 0x22002;
static GLFW_CONTEXT_VERSION_MINOR: u32 = 0x22003;
static GLFW_OPENGL_FORWARD_COMPAT: u32 = 0x22006;
static GLFW_OPENGL_PROFILE: u32 = 0x22008;
static GLFW_OPENGL_CORE_PROFILE: i32 = 0x00032001;

type FnCreateVertexArrays = extern "C" fn(n: libc::c_int, arrays: *mut libc::c_uint);

fn main() {
    let w_name = CString::new("Test").unwrap();
    let fn_name = CString::new("glCreateVertexArrays").unwrap();

    unsafe {
        glfwInit();

        // Set OpenGL Version
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

        // Select core profile
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1);

        // Create Window
        let window = glfwCreateWindow(
            400,
            400,
            w_name.as_ptr(),
            ptr::null_mut(),
            ptr::null_mut(),
        );
        glfwMakeContextCurrent(window);
    }

    let create_vertex_arrays: FnCreateVertexArrays = unsafe {
        glXGetProcAddress(fn_name.as_ptr())
    };

    let mut vao: libc::c_uint = 0;
    unsafe {
        (create_vertex_arrays)(1, &mut vao);
    }

    println!("Hello, world!");
}

但是,转换不正确:

error[E0308]: mismatched types
  --> src/main.rs:63:9
   |
63 |         glXGetProcAddress(fn_name.as_ptr())
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
   |
   = note: expected type `extern "C" fn(i32, *mut u32)`
              found type `extern "C" fn()`

这里的主要问题是 OpenGL 函数 glXGetProcAddress 返回一个 void*() 指针,必须将其转换为适当的函数指针类型。这怎么可能?

我知道已经有 OpenGL/GLFW 的包装器和引擎,但我想这样做是为了学习。

最佳答案

这是一个稍小的复制品:

extern crate libc;

extern "C" {
    fn getProcAddress(procname: libc::c_int) -> extern "C" fn();
}

type CreateVertexArrays = extern "C" fn(n: libc::c_int, arrays: *mut libc::c_uint);

fn main() {
    let create_vertex_arrays: CreateVertexArrays = unsafe {
        getProcAddress(42)
    };

    let mut vao = 0;
    (create_vertex_arrays)(1, &mut vao);
}

一个解决方案是“将这种类型变成那种类型”的大锤:mem::transmute :

fn main() {
    let create_vertex_arrays: CreateVertexArrays = unsafe {
        ::std::mem::transmute(getProcAddress(42))
    };

    let mut vao = 0;
    (create_vertex_arrays)(1, &mut vao);
}

但是 mem::transmute 确实应该用作最后的手段。我所知道的其他转换技术似乎都不适用于此处...


另见:

关于rust - 如何调用返回多种类型的不同函数指针的 C 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45514870/

相关文章:

rust - 有没有办法强制从特定堆栈帧返回后不使用 Rust 原始指针?

rust - 如何创建一个全局的、可变的单例?

rust - Const 闭包数组采用对 Rust 中具有生命周期参数的结构的可变引用

macros - 是否可以防止 Rust 中的宏重复相同的参数?

string - Luajit 相当于 string.pack 和 string.unpack?

c++ - 在 C 和我的语言之间创建 FFI

rust - 我什么时候应该在 C 库的 Rust 绑定(bind)中使用 `&mut self` 与 `&self`?

macros - 如何将 Rust 宏变量嵌入到文档中?

rust - 无法在Windows Rust上建立bevy

haskell - 将 OCaml 代码与共享库链接