Skip to main content

raw_wstp_function/
raw_wstp_function.rs

1//! This example demonstrates using the raw Rust wrappers around the LibraryLink C API to
2//! write a function which looks much like a classic C function using LibraryLink and
3//! WSTP would.
4//!
5//! This also includes an example of mixing the low-level LibraryLink bindings with the
6//! higher-level bindings provided by the `wstp` crate.
7
8use std::os::raw::{c_int, c_uint};
9
10use wolfram_library_link::{
11    expr::{expr, Expr},
12    sys::{
13        self as wll_sys, WolframLibraryData, LIBRARY_FUNCTION_ERROR, LIBRARY_NO_ERROR,
14    },
15    wstp::{
16        sys::{WSGetInteger, WSNewPacket, WSPutInteger, WSTestHead, WSLINK},
17        Link,
18    },
19};
20
21/// This function is loaded by evaluating:
22///
23/// ```wolfram
24/// LibraryFunctionLoad[
25///     "/path/to/libraw_wstp_function.dylib",
26///     "demo_wstp_function",
27///     LinkObject,
28///     LinkObject
29/// ]
30/// ```
31#[no_mangle]
32pub unsafe extern "C" fn demo_wstp_function(
33    _lib: WolframLibraryData,
34    link: WSLINK,
35) -> c_uint {
36    let mut i1: c_int = 0;
37    let mut i2: c_int = 0;
38    let mut len: c_int = 0;
39
40    if WSTestHead(link, b"List\0".as_ptr() as *const i8, &mut len) == 0 {
41        return LIBRARY_FUNCTION_ERROR;
42    }
43    if len != 2 {
44        return LIBRARY_FUNCTION_ERROR;
45    }
46
47    if WSGetInteger(link, &mut i1) == 0 {
48        return LIBRARY_FUNCTION_ERROR;
49    }
50    if WSGetInteger(link, &mut i2) == 0 {
51        return LIBRARY_FUNCTION_ERROR;
52    }
53    if WSNewPacket(link) == 0 {
54        return LIBRARY_FUNCTION_ERROR;
55    }
56
57    let sum = i1 + i2;
58
59    if WSPutInteger(link, sum) == 0 {
60        return LIBRARY_FUNCTION_ERROR;
61    }
62
63    return LIBRARY_NO_ERROR;
64}
65
66/// This example shows how the raw Rust wrappers can be mixed with higher-level wrappers
67/// around the Wolfram Symbolic Transfer Protocal (WSTP) for conveniently calling back
68/// into the Kernel to perform an evaluation.
69///
70/// This function is loaded by evaluating:
71///
72/// ```wolfram
73/// LibraryFunctionLoad[
74///     "/path/to/libraw_wstp_function.dylib",
75///     "demo_wstp_function_callback",
76///     LinkObject,
77///     LinkObject
78/// ]
79/// ```
80#[no_mangle]
81pub extern "C" fn demo_wstp_function_callback(
82    lib: WolframLibraryData,
83    mut link: WSLINK,
84) -> c_uint {
85    // Create a safe Link wrapper around the raw `WSLINK`. This is a borrowed rather than
86    // owned Link because the caller (the Kernel) owns the link.
87    let link: &mut Link = unsafe { Link::unchecked_ref_cast_mut(&mut link) };
88
89    // Skip reading the argument list packet.
90    if link.raw_get_next().and_then(|_| link.new_packet()).is_err() {
91        return LIBRARY_FUNCTION_ERROR;
92    }
93
94    let callback_link = unsafe { (*lib).getWSLINK.unwrap()(lib) };
95    let mut callback_link = callback_link as wstp::sys::WSLINK;
96
97    {
98        let safe_callback_link =
99            unsafe { Link::unchecked_ref_cast_mut(&mut callback_link) };
100
101        safe_callback_link
102            // EvaluatePacket[Print["Hello, World! --- WSTP"]]
103            .put_expr(&expr!(
104                System::EvaluatePacket[System::Print["Hello, World! --- WSTP"]]
105            ))
106            .unwrap();
107
108        unsafe {
109            (*lib).processWSLINK.unwrap()(
110                safe_callback_link.raw_link() as wll_sys::WSLINK
111            );
112        }
113
114        // Skip the return value packet. This is necessary, otherwise the link has
115        // unread data and the return value of this function cannot be processed properly.
116        if safe_callback_link
117            .raw_get_next()
118            .and_then(|_| safe_callback_link.new_packet())
119            .is_err()
120        {
121            return LIBRARY_FUNCTION_ERROR;
122        }
123    }
124
125    link.put_expr(&Expr::string("returned normally")).unwrap();
126
127    return LIBRARY_NO_ERROR;
128}
129
130/// This example makes use of the [`wstp`][wstp] crate to provide a safe wrapper around
131/// around the WSTP link object, which can be used to read the argument expression and
132/// write out the return expression.
133///
134/// ```wolfram
135/// function = LibraryFunctionLoad[
136///     "raw_wstp_function",
137///     "wstp_expr_function",
138///     LinkObject,
139///     LinkObject
140/// ];
141/// ```
142#[no_mangle]
143pub extern "C" fn wstp_expr_function(
144    _lib: WolframLibraryData,
145    mut unsafe_link: WSLINK,
146) -> c_uint {
147    let link: &mut Link = unsafe { Link::unchecked_ref_cast_mut(&mut unsafe_link) };
148
149    let expr = match link.get_expr() {
150        Ok(expr) => expr,
151        Err(err) => {
152            // Skip reading the argument list packet.
153            if link.raw_get_next().and_then(|_| link.new_packet()).is_err() {
154                return LIBRARY_FUNCTION_ERROR;
155            }
156
157            let msg = err.to_string();
158            let err = wolfram_library_link::expr::expr!(
159                System::Failure["WSTP Error", {"Message" -> msg}]
160            );
161            match link.put_expr(&err) {
162                Ok(()) => return LIBRARY_NO_ERROR,
163                Err(_) => return LIBRARY_FUNCTION_ERROR,
164            }
165        },
166    };
167
168    let expr_string = format!("Input: {}", expr.to_string());
169
170    match link.put_expr(&Expr::string(expr_string)) {
171        Ok(()) => LIBRARY_NO_ERROR,
172        Err(_) => LIBRARY_FUNCTION_ERROR,
173    }
174}