    fn version(&self) -> String {
        let Some(fp) = self.vtable.version_fn else { return String::new() };
        let mut _out: *mut std::ffi::c_char = std::ptr::null_mut();
        // SAFETY: fp is valid; user_data validity is the caller's responsibility.
        unsafe { fp(self.user_data, &mut _out) };
        if _out.is_null() { return self.cached_version.clone(); }
        // SAFETY: _out is a callee-allocated CString; we take ownership.
        let cs = unsafe { std::ffi::CString::from_raw(_out) };
        cs.to_string_lossy().into_owned()
    }
