Skip to main content

wolfram_library_link/
args.rs

1//! Traits for working with data types that can be passed natively via LibraryLink
2//! [`MArgument`]s.
3
4use std::{
5    cell::RefCell,
6    ffi::{CStr, CString},
7    os::raw::c_char,
8};
9
10use ref_cast::RefCast;
11
12use crate::{
13    expr::{Expr, Symbol},
14    rtl,
15    sys::{self, mint, mreal, MArgument},
16    wstp::Link,
17    DataStore, Image, NumericArray,
18};
19
20/// Trait implemented for types that can be passed via an [`MArgument`].
21pub trait FromArg<'a> {
22    #[allow(missing_docs)]
23    unsafe fn from_arg(arg: &'a MArgument) -> Self;
24
25    /// Return the *LibraryLink* parameter type as a Wolfram Language expression.
26    ///
27    /// ```
28    /// use wolfram_library_link::{FromArg, NumericArray};
29    ///
30    /// assert_eq!(&bool::parameter_type().to_string(), "\"Boolean\"");
31    /// assert_eq!(&i64::parameter_type().to_string(), "System`Integer");
32    /// assert_eq!(
33    ///     &<&NumericArray<i8>>::parameter_type().to_string(),
34    ///     r#"System`List[System`LibraryDataType[System`NumericArray, "Integer8"], "Constant"]"#
35    /// );
36    /// ```
37    ///
38    /// See also [`IntoArg::return_type()`] and [`NativeFunction::signature()`].
39    fn parameter_type() -> Expr;
40}
41
42/// Trait implemented for types that can be returned via an [`MArgument`].
43///
44/// The [`MArgument`] that this trait is used to modify must be the return value of a
45/// LibraryLink function. It is not valid to modify [`MArgument`]s that contain
46/// LibraryLink function arguments.
47pub trait IntoArg {
48    /// Move `self` into `arg`.
49    ///
50    /// # Safety
51    ///
52    /// `arg` must be an uninitialized [`MArgument`] that is used to store the return
53    /// value of a LibraryLink function. The return type of that function must match
54    /// the type of `self.`
55    ///
56    /// This function must only be called immediately before returning from a LibraryLink
57    /// function. Each native LibraryLink function must perform at most one call to this
58    /// method per invocation.
59    //
60    // Private implementation note:
61    //   the "at most one call to this method per invocation" constraint is necessary to
62    //   maintain the safety invariants of `impl IntoArg for CString`.
63    unsafe fn into_arg(self, arg: MArgument);
64
65    /// Return the *LibraryLink* return type as a Wolfram Language expression.
66    ///
67    /// See also [`FromArg::parameter_type()`] and [`NativeFunction::signature()`].
68    fn return_type() -> Expr;
69}
70
71/// Trait implemented for any function whose parameters and return type are native
72/// LibraryLink [`MArgument`] types.
73///
74/// [`#[export]`][crate::export] can only be used with functions that implement this trait.
75///
76/// A function implements this trait if all of its parameters implement [`FromArg`] and
77/// its return type implements [`IntoArg`].
78///
79/// Functions that pass their arguments and return value using a [`wstp::Link`] do not
80/// implement this trait. See [`WstpFunction`].
81pub trait NativeFunction<'a> {
82    /// Call the function using the raw LibraryLink [`MArgument`] fields.
83    unsafe fn call(&self, args: &'a [MArgument], ret: MArgument);
84
85    /// Get the type signature of this function, suitable for use in
86    /// [`LibraryFunctionLoad`][ref/LibraryFunctionLoad]<code>[_, _, <i>parameters</i>, <i>ret</i>]</code>.
87    ///
88    /// [ref/LibraryFunctionLoad]: https://reference.wolfram.com/language/ref/LibraryFunctionLoad.html
89    ///
90    /// See also [`FromArg::parameter_type()`] and [`IntoArg::return_type()`].
91    ///
92    /// The function generated by [`generate_loader!`] uses this method to generate the
93    /// type signature for functions exported by [`#[export]`][crate::export].
94    // Note: This method takes `self` so that it is object safe.
95    fn signature(&self) -> Result<(Vec<Expr>, Expr), String>;
96}
97
98/// Trait implemented for any function whose parameters and return type can be passed
99/// over a WSTP [`Link`][crate::wstp::Link].
100///
101/// [`#[export(wstp)]`][crate::export#exportwstp] can only be used with functions that implement
102/// this trait.
103///
104/// A function implements this trait if its type signature is one of:
105///
106/// * `fn(_: &mut Link)`
107/// * `fn(_: Vec<Expr>) -> Expr`
108/// * `fn(_: Vec<Expr>)`
109pub trait WstpFunction {
110    /// Call the function using the [`Link`] object passed by the Kernel.
111    unsafe fn call(&self, link: &mut Link);
112}
113
114//======================================
115// FromArg Impls
116//======================================
117
118impl FromArg<'_> for bool {
119    unsafe fn from_arg(arg: &MArgument) -> Self {
120        crate::bool_from_mbool(*arg.boolean)
121    }
122
123    fn parameter_type() -> Expr {
124        Expr::string("Boolean")
125    }
126}
127
128impl FromArg<'_> for mint {
129    unsafe fn from_arg(arg: &MArgument) -> Self {
130        *arg.integer
131    }
132
133    fn parameter_type() -> Expr {
134        Expr::symbol(Symbol::new("System`Integer"))
135    }
136}
137
138impl FromArg<'_> for mreal {
139    unsafe fn from_arg(arg: &MArgument) -> Self {
140        *arg.real
141    }
142
143    fn parameter_type() -> Expr {
144        Expr::symbol(Symbol::new("System`Real"))
145    }
146}
147
148impl FromArg<'_> for sys::mcomplex {
149    unsafe fn from_arg(arg: &MArgument) -> Self {
150        *arg.cmplex
151    }
152
153    fn parameter_type() -> Expr {
154        Expr::symbol(Symbol::new("System`Complex"))
155    }
156}
157
158//--------------------------------------
159// Strings
160//--------------------------------------
161
162unsafe fn c_str_from_arg<'a>(arg: &'a MArgument) -> &'a CStr {
163    let cstr: *mut c_char = *arg.utf8string;
164    CStr::from_ptr(cstr)
165}
166
167impl<'a> FromArg<'a> for CString {
168    unsafe fn from_arg(arg: &'a MArgument) -> CString {
169        let owned = {
170            let cstr: &'a CStr = c_str_from_arg(arg);
171            CString::from(cstr)
172        };
173
174        // Now that we own our own copy of the string, disown the Kernel's copy.
175        rtl::UTF8String_disown(*arg.utf8string);
176
177        owned
178    }
179
180    fn parameter_type() -> Expr {
181        Expr::symbol(Symbol::new("System`String"))
182    }
183}
184
185/// # Panics
186///
187/// This conversion will panic if the [`MArgument::utf8string`] field is not valid UTF-8.
188impl<'a> FromArg<'a> for String {
189    unsafe fn from_arg(arg: &'a MArgument) -> String {
190        let owned = {
191            let cstr: &'a CStr = c_str_from_arg(arg);
192            let str: &'a str = cstr
193                .to_str()
194                .expect("FromArg for &str: string was not valid UTF-8");
195            str.to_owned()
196        };
197
198        // Now that we own our own copy of the string, disown the Kernel's copy.
199        rtl::UTF8String_disown(*arg.utf8string);
200
201        owned
202    }
203
204    fn parameter_type() -> Expr {
205        Expr::symbol(Symbol::new("System`String"))
206    }
207}
208
209// TODO: Supported borrowed &CStr and &str's using some kind of wrapper that ensures we
210//       disown the Kernel string.
211
212/// # Safety
213///
214/// The lifetime of the returned `&CStr` must be the same as the lifetime of `arg`.
215///
216/// # Warning
217///
218/// Using `&CStr` as the parameter type of a *LibraryLink* function will result in a
219/// memory leak. Use [`String`] or [`CString`] instead.
220impl<'a> FromArg<'a> for &'a CStr {
221    unsafe fn from_arg(arg: &'a MArgument) -> &'a CStr {
222        c_str_from_arg(arg)
223    }
224
225    fn parameter_type() -> Expr {
226        // This type implements `FromArg` purely for usage in DataStoreNode::value()
227        // (via `FromArg for &str`).
228        panic!("&CStr cannot be used as a LibraryLink function parameter type")
229    }
230}
231
232/// # Panics
233///
234/// This conversion will panic if the [`MArgument::utf8string`] field is not valid UTF-8.
235///
236/// # Safety
237///
238/// The lifetime of the returned `&str` must be the same as the lifetime of `arg`.
239///
240/// # Warning
241///
242/// Using `&str` as the parameter type of a *LibraryLink* function will result in a
243/// memory leak. Use [`String`] or [`CString`] instead.
244impl<'a> FromArg<'a> for &'a str {
245    unsafe fn from_arg(arg: &'a MArgument) -> &'a str {
246        let cstr: &'a CStr = FromArg::<'a>::from_arg(arg);
247        cstr.to_str()
248            .expect("FromArg for &str: string was not valid UTF-8")
249    }
250
251    fn parameter_type() -> Expr {
252        // This type implements `FromArg` purely for usage in DataStoreNode::value().
253        panic!("&str cannot be used as a LibraryLink function parameter type")
254    }
255}
256
257//--------------------------------------
258// NumericArray
259//--------------------------------------
260
261// TODO: Add FromArg for NumericArray which just clones the numeric array? Or disclaims
262//       ownership in another way?
263
264
265/// # Safety
266///
267/// `FromArg for NumericArray<T>` MUST be constrained by `T: NumericArrayType` to prevent
268/// accidental creation of invalid `NumericArray` conversions. Without this constraint,
269/// it would be possible to write code like:
270///
271/// ```compile_fail
272/// # mod scope {
273/// # use wolfram_library_link::{export, NumericArray};
274/// #[export] // Unsafe!
275/// fn and(bools: NumericArray<bool>) -> bool {
276///     // ...
277/// #   todo!()
278/// }
279/// # }
280/// ```
281///
282/// which is not valid because `bool` is not a valid numeric array type.
283impl<'a, T: crate::NumericArrayType> FromArg<'a> for &'a NumericArray<T> {
284    unsafe fn from_arg(arg: &'a MArgument) -> &'a NumericArray<T> {
285        NumericArray::ref_cast(&*arg.numeric)
286    }
287
288    fn parameter_type() -> Expr {
289        // NOTE:
290        //   We use "Constant" instead of Automatic as the default memory management
291        //   strategy for &NumericArray<T> (and &Image<T> as well). This is because, for
292        //   both Automatic and "Constant", the fact remains that on the Rust side we have
293        //   an immutable reference to a NumericArray -- we're not going to free the array,
294        //   and we're not going to mutate. Using Automatic would behave correctly, but
295        //   would incur an unnecessary deep clone of the array contents as Automatic
296        //   doesn't imply any explicit promise from the programmer that they will not
297        //   mutate the array (unlike "Constant", which *is* an explicit promise that we
298        //   won't mutate the array the Kernel passes in).
299
300        // {LibraryDataType[NumericArray, "<T>"], "Constant"}
301        Expr::normal(Symbol::new("System`List"), vec![
302            Expr::normal(Symbol::new("System`LibraryDataType"), vec![
303                Expr::from(Symbol::new("System`NumericArray")),
304                Expr::string(T::TYPE.name()),
305            ]),
306            Expr::string("Constant"),
307        ])
308    }
309}
310
311impl<'a, T: crate::NumericArrayType> FromArg<'a> for NumericArray<T> {
312    unsafe fn from_arg(arg: &'a MArgument) -> NumericArray<T> {
313        NumericArray::from_raw(*arg.numeric)
314    }
315
316    fn parameter_type() -> Expr {
317        // {LibraryDataType[NumericArray, "<T>"], "Shared"}
318        Expr::normal(Symbol::new("System`List"), vec![
319            Expr::normal(Symbol::new("System`LibraryDataType"), vec![
320                Expr::from(Symbol::new("System`NumericArray")),
321                Expr::string(T::TYPE.name()),
322            ]),
323            Expr::string("Shared"),
324        ])
325    }
326}
327
328impl<'a> FromArg<'a> for &'a NumericArray<()> {
329    unsafe fn from_arg(arg: &'a MArgument) -> &'a NumericArray<()> {
330        NumericArray::ref_cast(&*arg.numeric)
331    }
332
333    fn parameter_type() -> Expr {
334        // {NumericArray, "Constant"}
335        Expr::normal(Symbol::new("System`List"), vec![
336            Expr::from(Symbol::new("System`NumericArray")),
337            Expr::string("Constant"),
338        ])
339    }
340}
341
342impl<'a> FromArg<'a> for NumericArray<()> {
343    unsafe fn from_arg(arg: &'a MArgument) -> NumericArray<()> {
344        NumericArray::from_raw(*arg.numeric)
345    }
346
347    fn parameter_type() -> Expr {
348        // {NumericArray, "Shared"}
349        Expr::normal(Symbol::new("System`List"), vec![
350            Expr::from(Symbol::new("System`NumericArray")),
351            Expr::string("Shared"),
352        ])
353    }
354}
355
356//--------------------------------------
357// Image
358//--------------------------------------
359
360impl<'a, T: crate::ImageData> FromArg<'a> for &'a Image<T> {
361    unsafe fn from_arg(arg: &'a MArgument) -> &'a Image<T> {
362        Image::ref_cast(&*arg.image)
363    }
364
365    fn parameter_type() -> Expr {
366        // {LibraryDataType[Image | Image3D, "<T>"], "Constant"}
367        Expr::normal(Symbol::new("System`List"), vec![
368            Expr::normal(Symbol::new("System`LibraryDataType"), vec![
369                Expr::normal(Symbol::new("System`Alternatives"), vec![
370                    Expr::from(Symbol::new("System`Image")),
371                    Expr::from(Symbol::new("System`Image3D")),
372                ]),
373                Expr::string(T::TYPE.name()),
374            ]),
375            Expr::string("Constant"),
376        ])
377    }
378}
379
380impl<'a, T: crate::ImageData> FromArg<'a> for Image<T> {
381    unsafe fn from_arg(arg: &'a MArgument) -> Image<T> {
382        Image::from_raw(*arg.image)
383    }
384
385    fn parameter_type() -> Expr {
386        // {LibraryDataType[Image | Image3D, "<T>"], "Shared"}
387        Expr::normal(Symbol::new("System`List"), vec![
388            Expr::normal(Symbol::new("System`LibraryDataType"), vec![
389                Expr::normal(Symbol::new("System`Alternatives"), vec![
390                    Expr::from(Symbol::new("System`Image")),
391                    Expr::from(Symbol::new("System`Image3D")),
392                ]),
393                Expr::string(T::TYPE.name()),
394            ]),
395            Expr::string("Shared"),
396        ])
397    }
398}
399
400impl<'a> FromArg<'a> for &'a Image<()> {
401    unsafe fn from_arg(arg: &'a MArgument) -> &'a Image<()> {
402        Image::ref_cast(&*arg.image)
403    }
404
405    fn parameter_type() -> Expr {
406        // {Image | Image3D, "Constant"}
407        Expr::normal(Symbol::new("System`List"), vec![
408            Expr::normal(Symbol::new("System`Alternatives"), vec![
409                Expr::from(Symbol::new("System`Image")),
410                Expr::from(Symbol::new("System`Image3D")),
411            ]),
412            Expr::string("Constant"),
413        ])
414    }
415}
416
417impl<'a> FromArg<'a> for Image<()> {
418    unsafe fn from_arg(arg: &'a MArgument) -> Image<()> {
419        Image::from_raw(*arg.image)
420    }
421
422    fn parameter_type() -> Expr {
423        // {Image | Image3D, "Shared"}
424        Expr::normal(Symbol::new("System`List"), vec![
425            Expr::normal(Symbol::new("System`Alternatives"), vec![
426                Expr::from(Symbol::new("System`Image")),
427                Expr::from(Symbol::new("System`Image3D")),
428            ]),
429            Expr::string("Shared"),
430        ])
431    }
432}
433
434//--------------------------------------
435// DataStore
436//--------------------------------------
437
438impl FromArg<'_> for DataStore {
439    unsafe fn from_arg(arg: &MArgument) -> DataStore {
440        DataStore::from_raw(*arg.tensor as sys::DataStore)
441    }
442
443    fn parameter_type() -> Expr {
444        Expr::string("DataStore")
445    }
446}
447
448impl<'a> FromArg<'a> for &'a DataStore {
449    unsafe fn from_arg(arg: &MArgument) -> &'a DataStore {
450        DataStore::ref_cast(&*(arg.tensor as *mut sys::DataStore))
451    }
452
453    fn parameter_type() -> Expr {
454        // This type implements `FromArg` purely for usage in DataStoreNode::value().
455        panic!("&DataStore cannot be used as a LibraryLink function parameter type")
456    }
457}
458
459//======================================
460// impl IntoArg
461//======================================
462
463impl IntoArg for () {
464    unsafe fn into_arg(self, _arg: MArgument) {
465        // Do nothing.
466    }
467
468    fn return_type() -> Expr {
469        Expr::string("Void")
470    }
471}
472
473//---------------------
474// Primitive data types
475//---------------------
476
477impl IntoArg for bool {
478    unsafe fn into_arg(self, arg: MArgument) {
479        let boole: u32 = if self { sys::True } else { sys::False };
480        *arg.boolean = boole as sys::mbool;
481    }
482
483    fn return_type() -> Expr {
484        Expr::string("Boolean")
485    }
486}
487
488impl IntoArg for mint {
489    unsafe fn into_arg(self, arg: MArgument) {
490        *arg.integer = self;
491    }
492
493    fn return_type() -> Expr {
494        Expr::symbol(Symbol::new("System`Integer"))
495    }
496}
497
498impl IntoArg for mreal {
499    unsafe fn into_arg(self, arg: MArgument) {
500        *arg.real = self;
501    }
502
503    fn return_type() -> Expr {
504        Expr::symbol(Symbol::new("System`Real"))
505    }
506}
507
508impl IntoArg for sys::mcomplex {
509    unsafe fn into_arg(self, arg: MArgument) {
510        *arg.cmplex = self;
511    }
512
513    fn return_type() -> Expr {
514        Expr::symbol(Symbol::new("System`Complex"))
515    }
516}
517
518//--------------------------------------------------
519// Convenience conversions for narrow integer sizes.
520//--------------------------------------------------
521
522impl IntoArg for i8 {
523    unsafe fn into_arg(self, arg: MArgument) {
524        *arg.integer = mint::from(self);
525    }
526
527    fn return_type() -> Expr {
528        Expr::symbol(Symbol::new("System`Integer"))
529    }
530}
531
532impl IntoArg for i16 {
533    unsafe fn into_arg(self, arg: MArgument) {
534        *arg.integer = mint::from(self);
535    }
536
537    fn return_type() -> Expr {
538        Expr::symbol(Symbol::new("System`Integer"))
539    }
540}
541
542impl IntoArg for i32 {
543    unsafe fn into_arg(self, arg: MArgument) {
544        *arg.integer = mint::from(self);
545    }
546
547    fn return_type() -> Expr {
548        Expr::symbol(Symbol::new("System`Integer"))
549    }
550}
551
552impl IntoArg for u8 {
553    unsafe fn into_arg(self, arg: MArgument) {
554        *arg.integer = mint::from(self);
555    }
556
557    fn return_type() -> Expr {
558        Expr::symbol(Symbol::new("System`Integer"))
559    }
560}
561
562impl IntoArg for u16 {
563    unsafe fn into_arg(self, arg: MArgument) {
564        *arg.integer = mint::from(self);
565    }
566
567    fn return_type() -> Expr {
568        Expr::symbol(Symbol::new("System`Integer"))
569    }
570}
571
572// If we're on a 32 bit platform, mint might be an alias for i32. Avoid providing this
573// conversion on those platforms.
574#[cfg(target_pointer_width = "64")]
575impl IntoArg for u32 {
576    unsafe fn into_arg(self, arg: MArgument) {
577        *arg.integer = mint::from(self);
578    }
579
580    fn return_type() -> Expr {
581        Expr::symbol(Symbol::new("System`Integer"))
582    }
583}
584
585//--------------------
586// Strings
587//--------------------
588
589thread_local! {
590    /// See [`<CString as IntoArg>::into_arg()`] for information about how this static is
591    /// used.
592    static RETURNED_STRING: RefCell<Option<CString>> = RefCell::new(None);
593}
594
595impl IntoArg for CString {
596    unsafe fn into_arg(self, arg: MArgument) {
597        // Extend the lifetime of `self.as_ptr()` by storing `self` in `RETURNED_STRING`.
598        //
599        // This will keep `raw` valid past the point that the current LibraryLink
600        // function returns, at which point it will be copied by the Kernel and is no
601        // longer used. We'll drop `self` the next time this function is called.
602        //
603        // This implementation limits the maximum number of "leaked" strings to just one.
604        //
605        // For more information on management of string memory when passed via
606        // LibraryLink, see:
607        //
608        // <https://reference.wolfram.com/language/LibraryLink/tutorial/InteractionWithWolframLanguage.html#262826222>
609        let raw: *const c_char = RETURNED_STRING.with(|stored| {
610            // Drop the previously returned string, if any.
611            if let Some(prev) = stored.replace(None) {
612                drop(prev);
613            }
614
615            let raw: *const c_char = self.as_ptr();
616
617            *stored.borrow_mut() = Some(self);
618
619            raw
620        });
621
622        // Return `raw` via this MArgument.
623        *arg.utf8string = raw as *mut c_char;
624    }
625
626    fn return_type() -> Expr {
627        Expr::from(Symbol::new("System`String"))
628    }
629}
630
631impl IntoArg for String {
632    /// # Panics
633    ///
634    /// This function will panic if `self` cannot be converted into a [`CString`].
635    unsafe fn into_arg(self, arg: MArgument) {
636        let cstring = CString::new(self)
637            .expect("IntoArg for String: could not convert String to CString");
638
639        <CString as IntoArg>::into_arg(cstring, arg)
640    }
641
642    fn return_type() -> Expr {
643        Expr::from(Symbol::new("System`String"))
644    }
645}
646
647//---------------------------------------
648// NumericArray, Image, DataStore
649//---------------------------------------
650
651impl<T: crate::NumericArrayType> IntoArg for NumericArray<T> {
652    unsafe fn into_arg(self, arg: MArgument) {
653        *arg.numeric = self.into_raw();
654    }
655
656    fn return_type() -> Expr {
657        // LibraryDataType[NumericArray, "<T>"]
658        Expr::normal(Symbol::new("System`LibraryDataType"), vec![
659            Expr::from(Symbol::new("System`NumericArray")),
660            Expr::string(T::TYPE.name()),
661        ])
662    }
663}
664
665impl IntoArg for NumericArray<()> {
666    unsafe fn into_arg(self, arg: MArgument) {
667        *arg.numeric = self.into_raw();
668    }
669
670    fn return_type() -> Expr {
671        // NumericArray
672        Expr::from(Symbol::new("System`NumericArray"))
673    }
674}
675
676impl<T: crate::ImageData> IntoArg for Image<T> {
677    unsafe fn into_arg(self, arg: MArgument) {
678        *arg.image = self.into_raw();
679    }
680
681    fn return_type() -> Expr {
682        // LibraryDataType[Image | Image3D, "<T>"]
683        Expr::normal(Symbol::new("System`List"), vec![
684            Expr::normal(Symbol::new("System`LibraryDataType"), vec![
685                Expr::normal(Symbol::new("System`Alternatives"), vec![
686                    Expr::from(Symbol::new("System`Image")),
687                    Expr::from(Symbol::new("System`Image3D")),
688                ]),
689                Expr::string(T::TYPE.name()),
690            ]),
691            Expr::string("Shared"),
692        ])
693    }
694}
695
696impl IntoArg for DataStore {
697    unsafe fn into_arg(self, arg: MArgument) {
698        *arg.tensor = self.into_raw() as *mut _;
699    }
700
701    fn return_type() -> Expr {
702        Expr::string("DataStore")
703    }
704}
705
706//======================================
707// impl NativeFunction
708//======================================
709
710/// Implement `NativeFunction` for functions that use raw [`MArgument`]s for their
711/// arguments and return value.
712///
713/// # Example
714///
715/// ```
716/// # mod scope {
717/// use wolfram_library_link::{self as wll, sys::MArgument, FromArg};
718///
719/// #[wll::export]
720/// fn raw_add2(args: &[MArgument], ret: MArgument) {
721///     let x = unsafe { i64::from_arg(&args[0]) };
722///     let y = unsafe { i64::from_arg(&args[1]) };
723///
724///     unsafe {
725///         *ret.integer = x + y;
726///     }
727/// }
728/// # }
729/// ```
730///
731/// ```wolfram
732/// LibraryFunctionLoad["...", "raw_add2", {Integer, Integer}, Integer]
733/// ```
734impl<'a: 'b, 'b> NativeFunction<'a> for fn(&'b [MArgument], MArgument) {
735    unsafe fn call(&self, args: &'a [MArgument], ret: MArgument) {
736        self(args, ret)
737    }
738
739    fn signature(&self) -> Result<(Vec<Expr>, Expr), String> {
740        Err(
741            "fn(&[MArgument], MArgument) function cannot be loaded automatically: \
742            parameter and return types are unknown."
743                .to_owned(),
744        )
745    }
746}
747
748//--------------------
749// impl NativeFunction
750//--------------------
751
752macro_rules! impl_NativeFunction {
753    ($($type:ident),*) => {
754        impl<'a, $($type,)* R> NativeFunction<'a> for fn($($type),*) -> R
755        where
756            R: IntoArg,
757            $($type: FromArg<'a>),*
758        {
759            unsafe fn call(&self, args: &'a [MArgument], ret: MArgument) {
760                // Re-use the $type name as the local variable names. E.g.
761                //     let A1 = A1::from_arg(..);
762                // This works because types and variable names are different namespaces.
763                #[allow(non_snake_case)]
764                let [$($type,)*] = match args {
765                    [$($type,)*] => [$($type,)*],
766                    _ => panic!(
767                        "LibraryLink function number of arguments ({}) does not match \
768                        number of parameters",
769                        args.len()
770                    ),
771                };
772
773                $(
774                    #[allow(non_snake_case)]
775                    let $type: $type = $type::from_arg($type);
776                )*
777
778                let result: R = self($($type,)*);
779
780                result.into_arg(ret);
781            }
782
783            fn signature(&self) -> Result<(Vec<Expr>, Expr), String> {
784                let mut param_tys = Vec::new();
785
786                $(
787                    param_tys.push($type::parameter_type());
788                )*
789
790                Ok((param_tys, R::return_type()))
791            }
792        }
793    }
794}
795
796// Handle the zero-arguments case specially.
797impl<'a, R> NativeFunction<'a> for fn() -> R
798where
799    R: IntoArg,
800{
801    unsafe fn call(&self, args: &[MArgument], ret: MArgument) {
802        if args.len() != 0 {
803            panic!(
804                "LibraryLink function number of arguments ({}) does not match number of \
805                parameters",
806                args.len()
807            );
808        }
809
810        let result = self();
811
812        result.into_arg(ret);
813    }
814
815    fn signature(&self) -> Result<(Vec<Expr>, Expr), String> {
816        Ok((Vec::new(), R::return_type()))
817    }
818}
819
820impl_NativeFunction!(A1);
821impl_NativeFunction!(A1, A2);
822impl_NativeFunction!(A1, A2, A3);
823impl_NativeFunction!(A1, A2, A3, A4);
824impl_NativeFunction!(A1, A2, A3, A4, A5);
825impl_NativeFunction!(A1, A2, A3, A4, A5, A6);
826impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7);
827impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8);
828impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
829impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
830impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
831impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
832
833//======================================
834// impl WstpFunction
835//======================================
836
837/// Implement [`WstpFunction`] for functions that use a [`Link`] for their arguments and
838/// return value.
839///
840/// # Example
841///
842/// ```
843/// # mod scope {
844/// use wolfram_library_link::{self as wll, wstp::Link};
845///
846/// #[wll::export(wstp)]
847/// fn add2_link(link: &mut Link) {
848///     let argc: usize = link.test_head("List").unwrap();
849///
850///     if argc != 2 {
851///         panic!("expected 2 arguments, got {}", argc);
852///     }
853///
854///     let x = link.get_i64().unwrap();
855///     let y = link.get_i64().unwrap();
856///
857///     link.put_i64(x + y).unwrap();
858/// }
859/// # }
860/// ```
861///
862/// ```wolfram
863/// LibraryFunctionLoad["...", "add2_link", LinkObject, LinkObject]
864/// ```
865impl WstpFunction for fn(&mut Link) {
866    unsafe fn call(&self, link: &mut Link) {
867        self(link)
868    }
869}
870
871/// Implement [`WstpFunction`] for functions that use [`Expr`] for their arguments and
872/// return value.
873///
874/// # Example
875///
876/// ```
877/// # mod scope {
878/// use wolfram_library_link::{self as wll, wstp::Link, expr::{Expr, ExprKind}};
879///
880/// #[wll::export(wstp)]
881/// fn add2(args: Vec<Expr>) -> Expr {
882///     if args.len() != 2 {
883///         panic!("expected 2 arguments, got {}", args.len());
884///     }
885///
886///     let x: i64 = match *args[0].kind() {
887///         ExprKind::Integer(value) => value,
888///         _ => panic!("expected 1st argument to be Integer, got: {}", args[0])
889///     };
890///     let y: i64 = match *args[1].kind() {
891///         ExprKind::Integer(value) => value,
892///         _ => panic!("expected 2nd argument to be Integer, got: {}", args[1])
893///     };
894///
895///     Expr::from(x + y)
896/// }
897/// # }
898/// ```
899///
900/// ```wolfram
901/// LibraryFunctionLoad["...", "add2", LinkObject, LinkObject]
902/// ```
903impl WstpFunction for fn(Vec<Expr>) -> Expr {
904    unsafe fn call(&self, link: &mut Link) {
905        let args: Vec<Expr> = match get_args_list(link) {
906            Ok(args) => args,
907            Err(message) => panic!("WstpFunction: {}", message),
908        };
909
910        let result: Expr = self(args);
911
912        match link.put_expr(&result) {
913            Ok(()) => (),
914            Err(err) => panic!(
915                "WstpFunction: WSTP error writing return expression to link: {}",
916                err
917            ),
918        }
919    }
920}
921
922impl WstpFunction for fn(Vec<Expr>) {
923    unsafe fn call(&self, link: &mut Link) {
924        let args: Vec<Expr> = match get_args_list(link) {
925            Ok(args) => args,
926            Err(message) => panic!("WstpFunction: {}", message),
927        };
928
929        let _null: () = self(args);
930
931        match link.put_symbol("System`Null") {
932            Ok(()) => (),
933            Err(err) => panic!(
934                "WstpFunction: WSTP error writing return Null expression to link: {}",
935                err
936            ),
937        }
938    }
939}
940
941//----------------------------
942// Utilities
943//----------------------------
944
945fn get_args_list(link: &mut Link) -> Result<Vec<Expr>, String> {
946    get_args_list_impl(link).map_err(|err: wstp::Error| {
947        format!("WSTP error reading argument List expression: {}", err)
948    })
949}
950
951fn get_args_list_impl(link: &mut Link) -> Result<Vec<Expr>, wstp::Error> {
952    let arg_count: usize = match link.test_head("List") {
953        Ok(count) => Ok(count),
954        Err(err) if err.code() == Some(wstp::sys::WSEGSEQ) => {
955            link.clear_error();
956            link.test_head("System`List")
957        },
958        Err(err) => Err(err),
959    }?;
960
961    let mut elements: Vec<Expr> = Vec::new();
962
963    for _ in 0..arg_count {
964        let elem = link.get_expr()?;
965        elements.push(elem);
966    }
967
968    Ok(elements)
969}