pub fn invoke(address: usize, arguments: &[u8]) -> Result<(), String>Expand description
Invokes a pre-loaded function using its memory address.
This function calls a C function that has been previously loaded with load.
Use this for better performance when calling the same function multiple times.
§Arguments
address- Memory address of the function (obtained fromload)arguments- Byte slice containing the serialized arguments
§Returns
Ok(())- Function was successfully invokedErr(String)- Error message (e.g., invalid address)
§Examples
use dolphin::{load, invoke};
// Load once
let addr = load("./mylib.dylib", "print_message").unwrap();
// Invoke multiple times
for i in 0..100 {
let msg = format!("Call {}", i);
invoke(addr, msg.as_bytes())?;
}§Safety
This is a safe wrapper that validates the address before calling. However, you must ensure:
- The address is valid and was obtained from
load - The function is still loaded in memory
- The arguments match the C function’s expectations
Examples found in repository?
examples/preload_example.rs (line 72)
61 fn invoke<T>(&self, function_name: &str, arguments: &[T]) -> Result<(), String> {
62 let address = self.get(function_name)
63 .ok_or_else(|| format!("Function '{}' not found in registry", function_name))?;
64
65 let arg_bytes = unsafe {
66 std::slice::from_raw_parts(
67 arguments.as_ptr() as *const u8,
68 arguments.len() * std::mem::size_of::<T>()
69 )
70 };
71
72 invoke(address, arg_bytes)
73 }
74
75 /// List all registered functions
76 fn list_functions(&self) {
77 println!("\nRegistered Functions:");
78 println!("{:-<60}", "");
79 for (name, address) in &self.functions {
80 println!(" {} -> 0x{:x}", name, address);
81 }
82 println!("{:-<60}", "");
83 }
84}
85
86fn main() {
87 println!("=== Dolphin Pre-Load Example ===\n");
88 println!("This example demonstrates loading all function addresses");
89 println!("at startup for better performance on repeated calls.\n");
90
91 let library_path = "./examples/libexample.dylib";
92 let mut registry = FunctionRegistry::new();
93
94 // Phase 1: Pre-load all functions at startup
95 println!("--- Phase 1: Pre-loading Functions ---");
96
97 let functions = vec![
98 "calculate_sum",
99 "print_message",
100 "reverse_string",
101 "count_characters",
102 "print_user",
103 "update_user_balance",
104 "print_product",
105 "process_order",
106 "print_user_list",
107 ];
108
109 let mut loaded_count = 0;
110 for func_name in &functions {
111 match registry.register(library_path, func_name) {
112 Ok(_) => loaded_count += 1,
113 Err(e) => eprintln!("✗ {}", e),
114 }
115 }
116
117 println!("\n✓ Successfully loaded {}/{} functions\n", loaded_count, functions.len());
118 registry.list_functions();
119
120 // Phase 2: Use pre-loaded functions (faster - no library loading overhead)
121 println!("\n--- Phase 2: Using Pre-loaded Functions ---\n");
122
123 // Example 1: Calculate sum
124 println!("Example 1: Calculate Sum");
125 let numbers = vec![5, 10, 15, 20, 25];
126 match registry.invoke("calculate_sum", &numbers) {
127 Ok(_) => println!("✓ Invoked calculate_sum\n"),
128 Err(e) => eprintln!("✗ Error: {}\n", e),
129 }
130
131 // Example 2: Print message
132 println!("Example 2: Print Message");
133 let message = "Pre-loaded function call!";
134 match registry.invoke("print_message", message.as_bytes()) {
135 Ok(_) => println!("✓ Invoked print_message\n"),
136 Err(e) => eprintln!("✗ Error: {}\n", e),
137 }
138
139 // Example 3: Reverse string
140 println!("Example 3: Reverse String");
141 let text = "Fast Pre-loaded Call";
142 match registry.invoke("reverse_string", text.as_bytes()) {
143 Ok(_) => println!("✓ Invoked reverse_string\n"),
144 Err(e) => eprintln!("✗ Error: {}\n", e),
145 }
146
147 // Example 4: User model
148 println!("Example 4: User Model");
149 let user = User::new(42, "John Doe", 30, 5000.00);
150 let user_bytes = unsafe {
151 std::slice::from_raw_parts(
152 &user as *const User as *const u8,
153 std::mem::size_of::<User>()
154 )
155 };
156 match registry.invoke("print_user", user_bytes) {
157 Ok(_) => println!("✓ Invoked print_user\n"),
158 Err(e) => eprintln!("✗ Error: {}\n", e),
159 }
160
161 // Example 5: Multiple calls to same function (demonstrating performance benefit)
162 println!("Example 5: Multiple Calls (demonstrating pre-load benefit)");
163 let messages = vec![
164 "First call",
165 "Second call",
166 "Third call",
167 "Fourth call",
168 ];
169
170 for (i, msg) in messages.iter().enumerate() {
171 match registry.invoke("print_message", msg.as_bytes()) {
172 Ok(_) => println!(" ✓ Call {} completed", i + 1),
173 Err(e) => eprintln!(" ✗ Call {} failed: {}", i + 1, e),
174 }
175 }
176
177 // Example 6: Direct address usage (lowest level)
178 println!("\n--- Example 6: Direct Address Usage ---");
179 if let Some(address) = registry.get("print_message") {
180 println!("Using address 0x{:x} directly:", address);
181 let direct_msg = "Direct address call!";
182 match invoke(address, direct_msg.as_bytes()) {
183 Ok(_) => println!("✓ Direct invoke successful\n"),
184 Err(e) => eprintln!("✗ Direct invoke failed: {}\n", e),
185 }
186 }
187
188 // Performance comparison note
189 println!("--- Performance Notes ---");
190 println!("Pre-loading benefits:");
191 println!(" • All functions loaded once at startup");
192 println!(" • No library loading overhead on each call");
193 println!(" • Ideal for applications making many FFI calls");
194 println!(" • Can validate all functions exist at startup");
195 println!(" • Functions remain loaded for entire program lifetime");
196
197 println!("\n=== Example completed ===");
198}More examples
examples/usage_example.rs (line 106)
52fn main() {
53 println!("=== Dolphin FFI Examples ===\n");
54
55 // ========================================
56 // PART 1: Using load_and_invoke (convenience method)
57 // ========================================
58 println!("=== PART 1: Using load_and_invoke ===\n");
59
60 // Example 1: Simple integer array
61 println!("--- Example 1: Calculate Sum ---");
62 let numbers = vec![10, 20, 30, 40, 50];
63 match load_and_invoke("./examples/libexample.dylib", "calculate_sum", &numbers) {
64 Ok(_) => println!("✓ Successfully invoked calculate_sum\n"),
65 Err(e) => eprintln!("✗ Error: {}\n", e),
66 }
67
68 // Example 2: String operations
69 println!("--- Example 2: Print Message ---");
70 let message = "Hello from Rust!";
71 match load_and_invoke("./examples/libexample.dylib", "print_message", message.as_bytes()) {
72 Ok(_) => println!("✓ Successfully invoked print_message\n"),
73 Err(e) => eprintln!("✗ Error: {}\n", e),
74 }
75
76 // Example 3: User model with load_and_invoke
77 println!("--- Example 3: Print User Model ---");
78 let user = User::new(1, "Alice Johnson", 28, 1500.50);
79 let user_bytes = unsafe {
80 std::slice::from_raw_parts(
81 &user as *const User as *const u8,
82 std::mem::size_of::<User>()
83 )
84 };
85 match load_and_invoke("./examples/libexample.dylib", "print_user", user_bytes) {
86 Ok(_) => println!("✓ Successfully invoked print_user\n"),
87 Err(e) => eprintln!("✗ Error: {}\n", e),
88 }
89
90 // ========================================
91 // PART 2: Using load() then invoke() separately
92 // ========================================
93 println!("\n=== PART 2: Using load() + invoke() separately ===\n");
94 println!("This approach is better when you need to call the same function multiple times.\n");
95
96 // Example 4: Load once, invoke multiple times
97 println!("--- Example 4: Load 'reverse_string' once, use multiple times ---");
98 match load("./examples/libexample.dylib", "reverse_string") {
99 Some(reverse_addr) => {
100 println!("✓ Loaded 'reverse_string' at address: 0x{:x}\n", reverse_addr);
101
102 // Now invoke it multiple times with different strings
103 let strings = vec!["Dolphin", "FFI", "Library", "Rust"];
104 for (i, text) in strings.iter().enumerate() {
105 println!(" Call {}: Reversing '{}'", i + 1, text);
106 match invoke(reverse_addr, text.as_bytes()) {
107 Ok(_) => println!(" ✓ Success\n"),
108 Err(e) => eprintln!(" ✗ Error: {}\n", e),
109 }
110 }
111 }
112 None => eprintln!("✗ Failed to load 'reverse_string'\n"),
113 }
114
115 // Example 5: Load multiple functions, then use them
116 println!("--- Example 5: Pre-load multiple functions ---");
117 let calc_sum_addr = load("./examples/libexample.dylib", "calculate_sum");
118 let print_msg_addr = load("./examples/libexample.dylib", "print_message");
119 let count_chars_addr = load("./examples/libexample.dylib", "count_characters");
120
121 if calc_sum_addr.is_some() && print_msg_addr.is_some() && count_chars_addr.is_some() {
122 println!("✓ All functions loaded successfully:");
123 println!(" - calculate_sum: 0x{:x}", calc_sum_addr.unwrap());
124 println!(" - print_message: 0x{:x}", print_msg_addr.unwrap());
125 println!(" - count_characters: 0x{:x}\n", count_chars_addr.unwrap());
126
127 // Use calculate_sum
128 println!("Using calculate_sum:");
129 let nums1 = vec![5, 10, 15];
130 let bytes1 = unsafe {
131 std::slice::from_raw_parts(
132 nums1.as_ptr() as *const u8,
133 nums1.len() * std::mem::size_of::<i32>()
134 )
135 };
136 invoke(calc_sum_addr.unwrap(), bytes1).ok();
137
138 // Use print_message
139 println!("\nUsing print_message:");
140 let msg = "Pre-loaded function!";
141 invoke(print_msg_addr.unwrap(), msg.as_bytes()).ok();
142
143 // Use count_characters
144 println!("\nUsing count_characters:");
145 let sample = "Rust FFI 2024";
146 invoke(count_chars_addr.unwrap(), sample.as_bytes()).ok();
147
148 println!("\n✓ All pre-loaded functions executed\n");
149 } else {
150 eprintln!("✗ Failed to load one or more functions\n");
151 }
152
153 // Example 6: Load and invoke with User model
154 println!("--- Example 6: User operations with load + invoke ---");
155 if let Some(print_user_addr) = load("./examples/libexample.dylib", "print_user") {
156 println!("✓ Loaded 'print_user' at 0x{:x}\n", print_user_addr);
157
158 // Create multiple users and print them using same loaded function
159 let users_to_print = vec![
160 User::new(10, "John", 30, 1000.00),
161 User::new(20, "Jane", 25, 2500.50),
162 User::new(30, "Jake", 40, 5000.75),
163 ];
164
165 for (i, user) in users_to_print.iter().enumerate() {
166 println!("Printing user {}:", i + 1);
167 let bytes = unsafe {
168 std::slice::from_raw_parts(
169 user as *const User as *const u8,
170 std::mem::size_of::<User>()
171 )
172 };
173 match invoke(print_user_addr, bytes) {
174 Ok(_) => println!("✓ User printed\n"),
175 Err(e) => eprintln!("✗ Error: {}\n", e),
176 }
177 }
178 } else {
179 eprintln!("✗ Failed to load 'print_user'\n");
180 }
181
182 // Example 7: Load and invoke with Product model
183 println!("--- Example 7: Product processing with load + invoke ---");
184 if let Some(process_order_addr) = load("./examples/libexample.dylib", "process_order") {
185 println!("✓ Loaded 'process_order' at 0x{:x}\n", process_order_addr);
186
187 // Process multiple orders
188 let orders = vec![
189 (Product::new(1, "Mouse", 29.99, 100), 5),
190 (Product::new(2, "Keyboard", 79.99, 50), 3),
191 (Product::new(3, "Monitor", 299.99, 20), 2),
192 ];
193
194 for (i, (product, qty)) in orders.iter().enumerate() {
195 println!("Processing order {}:", i + 1);
196 let mut order_data = Vec::new();
197 order_data.extend_from_slice(unsafe {
198 std::slice::from_raw_parts(
199 product as *const Product as *const u8,
200 std::mem::size_of::<Product>()
201 )
202 });
203 order_data.extend_from_slice(unsafe {
204 std::slice::from_raw_parts(
205 qty as *const i32 as *const u8,
206 std::mem::size_of::<i32>()
207 )
208 });
209
210 match invoke(process_order_addr, &order_data) {
211 Ok(_) => println!("✓ Order processed\n"),
212 Err(e) => eprintln!("✗ Error: {}\n", e),
213 }
214 }
215 } else {
216 eprintln!("✗ Failed to load 'process_order'\n");
217 }
218
219 // ========================================
220 // PART 3: Performance comparison
221 // ========================================
222 println!("\n=== PART 3: Performance Demonstration ===\n");
223 println!("--- Calling same function 5 times ---\n");
224
225 // Method 1: load_and_invoke (loads library each time)
226 println!("Method 1: Using load_and_invoke (loads each time)");
227 for i in 1..=5 {
228 let msg = format!("Message {}", i);
229 load_and_invoke("./examples/libexample.dylib", "print_message", msg.as_bytes()).ok();
230 }
231
232 println!("\nMethod 2: Using load once + invoke many times (faster)");
233 if let Some(addr) = load("./examples/libexample.dylib", "print_message") {
234 for i in 1..=5 {
235 let msg = format!("Fast call {}", i);
236 invoke(addr, msg.as_bytes()).ok();
237 }
238 }
239
240 println!("\n✓ Notice: Method 2 is more efficient for repeated calls!\n");
241
242 println!("=== All examples completed ===");
243}