I need to read a file into a null terminated string at compile time.
Working in Rust OpenGL. I have a shader source code stored in a separate file. The function that will eventually read the source is gl::ShaderSource
from the gl crate. All it needs is a pointer to a null terminated string (the std::ffi::CStr
type).
Typically the guides I have seen read the shader source file using include_str!
, then at run time allocate a whole new buffer of length +1, then copy the original source into the new buffer and put the terminating 0 at the end. I'd like to avoid all that redundant allocating and copying and just have the correctly null terminated string at compile time.
I realize it is somewhat petty to want to avoid an extra allocation for a short shader file, but the principle could apply to many other types of larger constants.
While scrolling through suggested questions during the preview I saw this: How do I expose a compile time generated static C string through FFI?
which led me to this solution:
let bytes1 = concat!(include_str!("triangle.vertex_shader"), "\0");
let bytes2 = bytes1.as_bytes();
let bytes3 = unsafe {
CStr::from_bytes_with_nul_unchecked(bytes2)
};
println!("{:?}", bytes3);
Does this accomplish avoiding the runtime allocation and copying?
Yes that should work as intended. If you want you can even bundle it into a simple macro.
macro_rules! include_cstr {
($file:expr) => {{
// Create as explicit constant to force from_bytes_with_nul_unchecked to
// perform compile time saftey checks.
const CSTR: &'static ::std::ffi::CStr = unsafe {
let input = concat!($file, "\0");
::std::ffi::CStr::from_bytes_with_nul_unchecked(input.as_bytes())
};
CSTR
}};
}
const VERTEX_SHADER: &'static CStr = include_cstr!("shaders/vert.glsl");
const FRAGMENT_SHADER: &'static CStr = include_cstr!("shaders/frag.glsl");