    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();
        let mut _out_error: *mut std::ffi::c_char = std::ptr::null_mut();
        // SAFETY: fp is valid; user_data validity is the caller's responsibility.
        let rc = unsafe { fp(self.user_data, &mut _out, &mut _out_error) };
        if rc != 0 {
            if !_out_error.is_null() {
                // SAFETY: _out_error is a callee-allocated NUL-terminated string.
                let _ = unsafe { std::ffi::CStr::from_ptr(_out_error) }.to_string_lossy();
                if let Some(free_fn) = self.vtable.free_string {
                    // SAFETY: free_fn is the vtable-provided destructor for callback strings.
                    unsafe { free_fn(_out_error) };
                }
            }
            return self.cached_version.clone();
        }
        if _out.is_null() { return self.cached_version.clone(); }
        // SAFETY: _out is a callee-allocated NUL-terminated string.
        let version = unsafe { std::ffi::CStr::from_ptr(_out) }.to_string_lossy().into_owned();
        if let Some(free_fn) = self.vtable.free_string {
            // SAFETY: free_fn is the vtable-provided destructor for callback strings.
            unsafe { free_fn(_out) };
        }
        version
    }
