I'm creating some experimental bindings for IUP (GUI lib'y).
I can successfully create an object that represents the lib'y. However, I only want one application-global object for this purpose, something like this:
// iup: Iup; // iup needs to be a static Iup object
pub fn get_iup() -> &Iup {
// if an Iup hasn't been created, do: iup = Iup::new();
&iup; // return a reference to the single iup object
}
Unfortunately, I can't work out how to do the above because I keep hitting lifetime issues.
Here's the code I have so far:
use crate::{xerr, xerror::{xerror, XResult}}; // just my own result type nothing fancy
use lazy_static::lazy_static;
use libloading::{Library, Symbol};
use std::env;
use std::ffi::CStr;
use std::os::raw::c_char;
use std::path::PathBuf;
use std::ptr;
use std::str;
lazy_static! {
pub(crate) static ref IUP: Library = Library::new(dll()).expect("Failed to find IUP library");
}
fn dll() -> PathBuf {
let exe = env::current_exe().expect("Failed to find exe's path");
let root = exe.parent().expect("Failed to find location of IUP library");
let mut root = root.to_path_buf();
if cfg!(windows) {
root.push("iup/windows/iup.dll");
} else {
root.push("iup/linux/libiup.so");
};
root
}
pub struct Iup<'a> {
_close: Symbol<'a, SigVrV>,
_version: Symbol<'a, SigVrpC>,
_set_global: Symbol<'a, SigpCpCrV>,
}
impl<'a> Iup<'a> {
pub fn new() -> XResult<Iup<'a>> {
let iup_open: Symbol<'a, SigpIpppCrI> =
unsafe { IUP.get(b"IupOpen\0").unwrap() };
if iup_open(ptr::null(), ptr::null()) != NOERROR {
xerr!("Failed to open IUP library");
}
let set_global: Symbol<SigpCpCrV> = unsafe {
IUP.get(b"IupSetGlobal\0").unwrap()
};
set_global(c_from_str(UTF8MODE), c_from_str(YES));
Ok(Iup {
_close: unsafe { IUP.get(b"IupClose\0").unwrap() },
_version: unsafe { IUP.get(b"IupVersion\0").unwrap() },
_set_global: set_global,
})
}
pub fn set_global(self, name: &str, value: &str) {
(self._set_global)(c_from_str(&name), c_from_str(&value));
}
pub fn version(self) -> String {
match c_to_string((self._version)()) {
Ok(v) => v,
Err(_) => "?".to_string(),
}
}
}
impl<'a> Drop for Iup<'a> {
fn drop(&mut self) {
(self._close)();
}
}
fn c_to_string(p: *const c_char) -> XResult<String> {
let c: &CStr = unsafe { CStr::from_ptr(p) };
let s: &str = c.to_str()?;
Ok(s.to_owned())
}
fn c_from_str(s: &str) -> *const c_char {
unsafe { &*(s as *const _ as *const [c_char]) }.as_ptr()
}
#[repr(C)] pub struct Ihandle { _private: [u8; 0] }
pub(crate) type SigpIpppCrI = extern "C" fn(*const i32, *const *const *const c_char) -> i32;
pub(crate) type SigpCpCrV = extern "C" fn(*const c_char, *const c_char);
pub(crate) type SigVrV = extern "C" fn();
pub(crate) type SigVrpC = extern "C" fn() -> *const c_char;
pub const ERROR: i32 = 1;
pub const NOERROR: i32 = 0;
pub const YES: &str = "YES";
const UTF8MODE: &str = "UTF8MODE";
Is it possible to create a get_iup()
function that will return the singleton with a static
lifetime so that I can use it everywhere?