2016-05-25 7 views
2

Angenommen ich folgende Rust Bibliothek:Ist es möglich, eine Rust-Funktion aufzurufen, die ein Vec von C nimmt?

// lib.rs 
#![crate_type = staticlib] 

#[no_mangle] 
pub extern fn do_something(number: i32) { 
    // something 
} 

#[no_mangle] 
pub extern fn do_something_else(collection: &Vec<i32>) { 
    // something 
} 

Ich weiß, dass, do_something von C zu nennen, würde ich muss nur eine extern Funktion deklarieren eine int32_t nehmen, aber ist es möglich, do_something_else zu nennen? Wenn das so ist, wie?

+1

Ich denke, Sie können Integer-Array-Zeiger auf die Rost-Funktion von C übergeben. Dann können Sie die Vektorscheibe zu Operationen verwenden. – noshusan

+0

@noshusan Du meinst etwas wie 'pub extern fn do_thing (slice: & [i32])' 'dann deklariere es in der C-Seite wie' void do_thing (int32_t slice []) '? –

+0

Ich bin mir nicht sicher, aber Sie können etwas tun wie 'Pub extern fn do_thing (slice: * [i32])' die Deklaration in der c-Seite wie 'void do_thing (int32_t * slice [])'. Hier verwenden Sie raw_pointer, so dass Sie einen unsicheren Block deklarieren müssen. – noshusan

Antwort

4

Sie kann, aber die bessere Frage ist sollte Sie?

Da kann man keine Vec von C konstruieren, würden Sie es in Rust bauen müssen und dann einen Zeiger auf C-C-Code zurückkehren würde den Zeiger auf die Vec besitzen und würde dann geben sie zurück, wenn do_something_else aufrufen.

Dann gibt es das Problem, dass Sie die Vec in C nicht ändern können, entweder durch Erstellen neuer FFI-Methoden, die alle Rust-Methoden spiegeln.

Sie sollten auch wahrscheinlich nicht nehmen weil Rust Referenzen sind garantiert nicht NULL zu sein, und es gibt nichts, was die erzwingt, wenn sie von C. genannt Es ist besser, eine *const Vec<i32> zu nehmen, behaupten, dass es nicht-NULL und wandle es in eine Referenz um.

Wahrscheinlich möchten Sie ein C-Array über die FFI-Grenze akzeptieren. C-Arrays ist ein Zeiger und eine Länge, so würden Sie beide akzeptieren und Rekonstitution eine Rust Scheibe (da Sie würde das Array nicht besitzen):

use std::slice; 

pub extern fn do_something_else(p: *const i32, len: libc::size_t) { 
    let slice = unsafe { 
     assert!(!p.is_null()); 
     slice::from_raw_parts(p, len) 
    }; 
} 

obligatorischer Link zu The Rust FFI Omnibus.


Wenn Sie wirklich benötigt zu tun, was Sie gefragt, wäre es wahrscheinlich so etwas wie folgt aussehen:

extern crate libc; 

#[no_mangle] 
pub extern fn make_vec() -> *mut Vec<i32> { 
    Box::into_raw(Box::new(Vec::new())) 
} 

#[no_mangle] 
pub extern fn add_number(vec: *mut Vec<i32>, val: libc::int32_t) { 
    let vec = unsafe { 
     assert!(!vec.is_null()); 
     &mut *vec 
    }; 

    vec.push(val);  
} 

#[no_mangle] 
pub extern fn print_vec(vec: *const Vec<i32>) { 
    let vec = unsafe { 
     assert!(!vec.is_null()); 
     &*vec 
    }; 

    println!("{:?}", vec);  
} 

#[no_mangle] 
pub extern fn drop_vec(vec: *mut Vec<i32>) { 
    unsafe { 
     assert!(!vec.is_null()); 
     Box::from_raw(vec); 
    } 
} 

und würden gerne (ungetestet) verwendet werden:

// Add extern declarations 

int main(int argc, char *argv[]) { 
    void *v = make_vec(); // Use a real typedef here 
    add_number(v, 42); 
    print_vec(v); 
    drop_vec(v); 
} 

Du solltest dies unter valgrind ausführen, um sicher zu gehen, dass ich nichts Dummes getan habe.

+0

Meine Absicht ist es, Code aus einem C++ - Projekt in Rust zu verschieben - einige Berechnungen in Rust durchzuführen, dann die Ergebnisse zu erhalten und sie auf der C++ Seite zu verwenden. Die Idee ist, ein C++ - Projekt schrittweise nach Rust zu verlegen. Was wäre ein besserer Ansatz? –

+0

@ Romário das ist eine schwierige Frage zu beantworten, ohne mehr zu wissen und wäre wahrscheinlich für SO off-topic. Wenn Sie C-Code hätten, könnte ich sagen, dass es sich lohnt, diese Art von Änderung vorzunehmen, denn dann könnten Sie Ihre handgemachte erweiterbare Vektorimplementierung löschen. C++ verfügt jedoch über Sammlungen in der Standardbibliothek, sodass Sie für das andere nur eine Standardbibliothek wechseln.Vielleicht könntest du eine Stufe höher versuchen und das * Objekt *, das den "std :: vector" enthält, nach Rust bewegen? – Shepmaster

+0

'Vielleicht könntest du eine Ebene höher versuchen und das Objekt, das den std :: vector enthält, nach Rust bewegen?' Hat der FFI Omnibus irgendwelche Hinweise darauf? –