I have some more problems with binding c++ libs and the Rust ones. Last time @alice (solution) helped me a lot by posting an example which showed me that I really needed to go down to int pointers and length to pass a vector from and to my Rust code. Now I have a more complex problem where I need to construct an object, keep it in my c++ lib and send queries to it which then the object needs to process. To emulate the problem i am posting a small cpp code, src organization and my "attempt" to do it (which does not work since I don't know how to approach in designing a solution )..
extern crate libc;
extern {
fn Obj(list: *const libc::c_int, len: libc::c_int); // obviously not working as it needs API
}
fn main() {
let vec = vec![1, 2, 3, 4, 5, 6];
// Problem starts here ...
unsafe { Obj(vec.as_ptr(), vec.len() as libc::c_int) };
// ONCE constructed and alive I would like to make
// get() queries : obj.get(3) -> returns 4
// WHAT is the proper approach here ?
}
I am not sure if you are looking at the problem correctly. Rust does not know how to handle C++ objects like that.
For object constructors, destructors, and public methods, there is a completely separate ABI with an implicit this pointer. Your extern block in Rust does not match that. Even worse, Rust cannot understand the C++ ABI used by the compiler, because every C++ compiler uses its own!
Because of this, all of the examples of C++ interop I know about have C wrappers for the actual method calls, construction, and destruction. For example:
// obj.cpp
class Obj { ...}
extern "C" {
Obj *create_object(int* items, int len) {
// ... turn the input args into a vector...
return new Obj(vec);
}
void free_object(Obj *obj) {
delete obj;
}
// wrap all the other methods as well...
}
Those functions can then be called from Rust with an extern block like you had. All of the Obj instances will reside on the C++ heap, and be borrowed by Rust.
I dimly remember a Rust presentation a while ago (I can't find it right now) which talked about how to simplify your C++ class enough so that Rust could actually own an instance of that class safely. It used a more advanced version of the opaque pointer pattern, but as someone who has not touched modern C++, it seemed to me like magic.
Sorry I couldn't be more help. I hope this at least points you in the right direction.
Thank you @jhwgh1968 ! U helped a lot!! I have just a quick follow-up if you could give one more pointer. My rewrite looks like this
// build.rs - stays the same
// src/ffi/obj.cpp
#include <iostream>
#include <vector>
using namespace std;
class Obj {
public:
Obj(vector<int> vec);
~Obj();
int get(int x);
private:
vector<int> pvec;
};
Obj::Obj(vector<int> vec){
pvec = vec;
}
Obj::~Obj(){
vector<int>().swap(pvec);
}
int Obj::get(int x) {
if (x < (int) pvec.size()){
return (int) pvec[x];
}else{
return (int) -1;
}
}
extern "C" {
Obj* create_object(int* items, int len) {
vector<int> vec(len);
for (int i = 0; i< len; i++){
vec[i] = items[i];
}
return new Obj(vec);
}
void free_object(Obj *obj) {
delete obj;
}
int get_data (Obj *obj, int x){
return (*obj).get(x);
}
}
// main.rs
extern crate libc;
use std::sync::Arc;
extern "C" {
pub fn create_object(items: *const libc::c_int, len: libc::c_int) -> *mut Obj ;
pub fn get_data(obj: *mut Obj, x: libc::c_int)-> libc::c_int;
pub fn delete_object(obj: *mut Obj);
}
struct MyObj {
raw: *mut Obj,
}
impl MyObj {
fn new( x: *const libc::c_int, y: libc::c_int) -> MyObj {
unsafe { MyObj { raw: create_object(x,y) } }
}
}
impl Drop for MyObj {
fn drop(&mut self) {
unsafe {
delete_object(self.raw);
}
}
}
fn main() {
let vec = vec![1, 2, 3, 4, 5, 6];
let value = Arc::new(MyObj::new(vec.as_ptr(), vec.len() as libc::c_int));
let another_value = value.clone();
println!("Shared counter: {}", Arc::strong_count(&value));
}
so i tried to wrap everything nice and tidy but I am getting an error
error[E0412]: cannot find type `Obj` in this scope
--> src/main.rs:5:79
|
5 | pub fn create_object(items: *const libc::c_int, len: libc::c_int) -> *mut Obj ;
| ^^^ not found in this scope
what am I missing ? (probably something dumb...) Also would this solution work or do you maybe have a better suggestion ?