I am trying to call a C function from rust. The function suppose to receive the command lines arguments then print it. I used cmake to compile the C code to a static archive. I write a build.rs script to referee to the static library location and to make the static linking to it.
// library.cpp
#include "library.h"
#include <iostream>
extern "C"{
void print_args(int argc, char *argv[]){
std::cout << "Have " << argc << " arguments:" << std::endl;
std::cout<<argv<<std::endl;
for (int i = 0; i < argc; i) {
std::cout << argv[i] << std::endl;
}
}
}
//library.h
extern "C"{
void print_args(int argc, char *argv[]);
}
//build.rs
pub fn main(){
println!("cargo:rustc-link-search=.../cmake-build-debug"); //library.a directory
println!("cargo:rustc-link-lib=static=stdc ");
println!("cargo:rustc-link-lib=static=library");
}
//main.rs
#[link(name = "library", kind = "static")]
extern "C" {
pub fn print_args(args: c_int, argsv: *const c_char);
}
fn main() {
let args = std::env::args()
.map(|arg| CString::new(arg).unwrap())
.collect::<Vec<CString>>();
let args_len: c_int = args.len() as c_int;
let c_args_ptr = args.as_ptr() as *const c_char;
unsafe { print_args(args_len, c_args_ptr) };
}
When running the rust code by the command cargo run "10" "11"
. it is only able to print the first argument which is the name of the program then the error
error: process didn't exit successfully: target\debug\static_library_binding_test.exe 10 11 (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
appears.
it is the output rust main.rs
Have 3 arguments:
target\debug\static_library_binding_test.exe
error: process didn't exit successfully: `target\debug\static_library_binding_test.exe 10 11` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
So, I need to know how can I pass the command line argument from rust to the c function.
CodePudding user response:
The problem is in this code:
let args = std::env::args()
.map(|arg| CString::new(arg).unwrap())
.collect::<Vec<CString>>();
// ...
let c_args_ptr = args.as_ptr() as *const c_char;
That creates a vector of CString
objects, which you then proceed to cast into a pointer. A CString
consists of two word-sized values, a pointer and a length, and cannot be interpreted as just a pointer. To get an actual array of pointers which print_args()
expects, you need to collect them separately:
let args = std::env::args()
.map(|arg| CString::new(arg).unwrap())
.collect::<Vec<CString>>();
let arg_ptrs: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
let args_len: c_int = args.len() as c_int;
unsafe { print_args(args_len, arg_ptrs.as_ptr()) };
Note that you'll need to declare print_args
as taking pointer to pointer:
#[link(name = "library", kind = "static")]
extern "C" {
pub fn print_args(args: c_int, argsv: *const *const c_char);
}