rustmex 0.6.4

Rustmex: providing convenient Rust bindings to Matlab MEX API's
Documentation
/*!
 * Functions and function handles for calling back to Matlab
 *
 * This module contains functionality to call back into Matlab. Both calling via a
 * `function_handle` or a named matlab function (such as "sqrt") are supported.
 */
use core::ops::{Deref, DerefMut};
use rustmex_core::{
	convert::{
		FromMatlabError,
	},
	mxArray,
	classid::ClassID,
	pointers::{
		MatlabPtr,
		MutMatlabPtr,
		MxArray,
	},
	shim::{
		rustmex_call_matlab as mexCallMATLAB,
	},

	MatlabClass,
	MutMatlabClass,
	OwnedMatlabClass,
};

use std::{
	ffi::{
		CString,
		CStr,
	}
};

/**
 * Call a named Matlab function, such as `sqrt`, or `fmincon`. If an error occurs,
 * control is returned to the matlab prompt. Note that this might leak memory.
 *
 * Note that this function converts the function name into a CString. If you already have
 * a null-terminated string available, consider using [`call_named_nul`].
 */
pub fn call_named<FnName>(f: FnName, nargout: usize, rhs: &[&mxArray])
	-> Result<Box<[Option<MxArray>]>, FunctionCallError>
where
	FnName: Into<Vec<u8>>,
{
	let f = CString::new(f).map_err(|_| FunctionCallError::BadEncoding)?;
	call_named_nul(&f, nargout, rhs).ok_or(FunctionCallError::CallFailed)
}

/**
 * Call a named Matlab function, named with a nul terminated byte string.
 */
pub fn call_named_nul(f: &CStr, nargout: usize, rhs: &[&mxArray])
	-> Option<Box<[Option<MxArray>]>>
{
	let lhs = vec![None; nargout].into_boxed_slice();

	let ret = unsafe {
		mexCallMATLAB(
			lhs.len().try_into().expect("Don't expect 4 billion return values"),
			lhs.as_ptr() as *mut *mut mxArray,
			rhs.len().try_into().expect("Don't expect 4 billion arguments"),
			rhs.as_ptr() as *mut *mut mxArray,
			f.as_ptr() as *const i8
		)
	};

	if ret == 0 {
		Some(lhs)
	} else {
		None
	}
}


/**
 * Typed pointer representing a MATLAB `function_handle`
 *
 * Can be created from an mxArray with the correct class, and can then be used to call
 * back into matlab.
 */
#[repr(transparent)]
#[derive(Debug)]
pub struct Function<F: MatlabPtr>(F);

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum FunctionCallError {
	BadEncoding,
	CallFailed,
}

impl<P> Deref for Function<P> where P: MatlabPtr {
	type Target = mxArray;

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

impl<P> DerefMut for Function<P> where P: MutMatlabPtr {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.0
	}
}


// Bytestring to call feval with without having to construct the bytestring every time.
const FEVAL: &[u8; 6] = b"feval\0";

impl<F> Function<F> where F: MatlabPtr + AsRef<mxArray> {
	/**
	 * Call the function handle with the specified input arguments and number of
	 * output arguments.
	 */
	// TODO: Add a check for too many arguments (limited by fifty in, fifty out).
	pub fn call(&self, nargout: usize, rhs: &[&mxArray])
		-> Option<Box<[Option<MxArray>]>>
	{
		// Since we're calling the function handle via feval, we need to
		// "push_front" the function_handle onto the left-hand-side array
		let rhs = {
			let mut v = Vec::with_capacity(rhs.len() + 1);
			v.push(self.0.as_ref());
			v.extend(rhs.iter().map(|x| x.as_ref()));
			v
		};

		// SAFETY: We already added the nul to FEVAL ourselves
		call_named_nul(unsafe { CStr::from_bytes_with_nul_unchecked(FEVAL) },
			nargout,
			&rhs)
	}
}

impl<P> MatlabClass<P> for Function<P> where P: MatlabPtr {
	fn from_mx_array(mx: P) -> Result<Self, FromMatlabError<P>> {
		if mx.numel() != 1 {
			return Err(FromMatlabError::new_badsize(mx));
		}

		if mx.class_id() == Ok(ClassID::Function) {
			Ok(Function(mx))
		} else {
			Err(FromMatlabError::new_badclass(mx))
		}
	}

	fn into_inner(self) -> P {
		self.0
	}

	fn inner(&self) -> &P {
		&self.0
	}

	type Owned = Function<MxArray>;
	fn duplicate(&self) -> Self::Owned {
		Function(self.0.duplicate())
	}
}

impl<P> MutMatlabClass<P> for Function<P> where P: MutMatlabPtr {
	type AsBorrowed<'a> = Function<&'a mxArray> where Self: 'a;
	fn as_borrowed<'a>(&'a self) -> Self::AsBorrowed<'a> {
		Function(self.0.deref())
	}

	fn inner_mut(&mut self) -> &mut P {
		&mut self.0
	}
}

impl OwnedMatlabClass for Function<MxArray> {
	type AsMutable<'a> = Function<&'a mut mxArray> where Self: 'a;
	fn as_mutable<'a>(&'a mut self) -> Self::AsMutable<'a> {
		Function(self.0.deref_mut())
	}
}