CString 到 *const *const c_char

问题描述

我在 Rust 代码调用了一些 FFI 函数。我遇到了一些问题,我相信这是因为我在将 CString 转换为 *const *const c_char 时犯了一个错误

函数签名是这样的,

foo(strings: *const *const c_char,num_of_strings: i32)

在 C 中我会做这样的事情,

char *str = "...";
foo(&str,1);

在 Rust 中,我能想到的最好的是

let str = CString::new(std::fs::read_to_string("path/to/file"));
foo(&str.as_ptr() as *const *const c_char,1);

这没有问题,但我想不出一种方法来验证 &str.as_ptr() 是否采用了空终止字符串的地址。我曾尝试在 Stack Overflow 和 Nomicon 中查找,但找不到任何要检查的内容

可能我的转换是正确的,但问题是别的,但我认为我支持 CStrings 的所有不变量,而且我正在读入 CString 的文件是复制粘贴的,所以它应该完全没问题.无论哪种方式,我都无法确定我是否正确投射,我真的很想知道正确的方法。另外,如果有什么地方可以扩展这种东西,我很乐意为您提供建议。

解决方法

我阅读了您调用的函数的 docs。它说:

指定指向包含要加载到着色器的源代码的字符串的指针数组。

所以你应该这样称呼它:

use std::os::raw::*;
use std::convert::TryInto;

// Docs says it is not necessary to string to be null terminated
// so don't need to use CString
let shader_code = std::fs::read_to_string("path/to/file").unwrap();
assert!(shader_code.is_ascii()); // I don't know if OpenGL supports utf8
let codes_array: [*const c_char; 1] = [shader_code.as_ptr() as _];
let codes_lengths: [c_int; 1] = [shader_code.len().try_into().unwrap()];

// I do not know exact api of your Rust wrapper
glShaderSource(
     shader_handle,codes_array.len().try_into().unwrap(),codes_array.as_ptr(),codes_lengths.as_ptr()
);

我只用 String 替换了 CString 的使用,因为文档说如果提供了长度,则字符串没有必要以 null 结尾。

如果 length 为 NULL,则假定每个字符串都以 null 结尾。如果 length 是非 NULL 值,则它指向一个数组,该数组包含 string 的每个相应元素的字符串长度。长度数组中的每个元素可能包含相应字符串的长度(空字符不计为字符串长度的一部分)或小于 0 的值以指示字符串以空字符结尾。此时不扫描或解析源代码字符串;它们被简单地复制到指定的着色器对象中。