2016-11-12 1 views
4

Ich habe folgenden C-Code enthält, der als .so zusammengestellt:Belichten ein C Symbol einen Null-terminierte Array von Funktionszeigern von Rust bis C

void (*vlog_startup_routines[])() = { 
    hello_register, 
    0 
}; 

In Rust, I kann mit #[no_mangle] Funktionen erklären. Wie würde ich ein Symbol mit dem Namen vlog_startup_routines verfügbar machen, bei dem es sich um ein Array handelt, das Funktionszeiger enthält und ebenfalls null terminiert?

+0

Interessant. Ein Array ist nichts anderes als ein Zeiger, und wenn 0 beendet wird, ist es eine Laufzeiteigenschaft. Ich bin mir jedoch nicht sicher, wie man den Zeiger auf Funktionen über die FFI-Grenze hinausgibt; Haben Sie die bestehende Rust-Dokumentation überprüft? –

+0

@MatthieuM. und der Array-Zeiger existiert hier grundsätzlich nicht; '(void *) hallo_register == (void *) vlog_startup_routines', oder? – Shepmaster

+0

Ich würde vorschlagen, an Ihren Formulierungen zu arbeiten, wenn Sie Fragen stellen. Durch die Bereitstellung von C-Code sieht es so aus, als ob Sie diesen Code * aufrufen möchten. Es wäre besser gewesen, einen C-Code bereitzustellen, der ein "extern" verwendet, um hervorzuheben, dass Sie dieses Symbol in Rust implementieren möchten. Außerdem, wenn Sie eine Bibliothek haben, gibt es nichts, um das Programm zu steuern, also würde nichts angerufen werden. Eine C-ausführbare Datei würde das Problem besser ausdrücken. – Shepmaster

Antwort

2

Sie müssen ein static Element definieren, dessen Typ ein Array ist. Bei der Definition eines Elements static müssen wir leider die Größe dieses Arrays angeben (ab Rust 1.13.0).

Funktionszeiger in Rust gelten nicht als unsicher zum Anrufen (es sei denn, Sie haben eine unsafe fn). Es ist jedoch nicht möglich, einen Nullzeiger aufzurufen, sodass Rust das Erstellen eines Nullfunktionszeigers nicht zulässt. Aber es gibt einen Trick: Wenn T ist ein Zeiger (welcher Art von Zeiger, einschließlich Fat Pointer und Funktionszeiger), Option<T> has the same size as T1, and None is simply represented as a null pointer. Daher können wir ein Array von Option<fn()> Werten definieren, um das gewünschte Ergebnis zu erhalten.

Für andere Typen wäre Option<T> größer als T, um die Diskriminante zu speichern.

#[no_mangle] 
#[allow(non_upper_case_globals)] 
pub static vlog_startup_routines: [Option<fn()>; 2] = [ 
    Some(hello_register), 
    None 
]; 

Wenn Sie die Arraygröße angeben müssen, die Sie ärgert, dann können Sie ein Makro verwenden, das es für Sie berechnet. Als Bonus fügt dieses Makro die nachfolgende None hinzu und umschließt jede Funktion in Some.

macro_rules! one_for { 
    ($_x:tt) => (1) 
} 

macro_rules! vlog_startup_routines { 
    ($($func:expr,)*) => { 
     #[no_mangle] 
     #[allow(non_upper_case_globals)] 
     pub static vlog_startup_routines: [Option<fn()>; $(one_for!($func) +)* 1] = [ 
      $(Some($func),)* 
      None 
     ]; 
    } 
} 

vlog_startup_routines! { 
    hello_register, 
} 

Hinweis: Die one_for Makro existiert, weil wir eines der Parametersymbole in einem Wiederholungsmuster verweisen müssen (Sie mehrere unterschiedliche Wiederholungen haben kann, so dass der Compiler die Sie sich beziehen man wissen muss) aber wir interessieren uns nicht für seinen Wert.

+0

'Keine'; Na sicher! Ich konnte nicht herausfinden, wie man einen "NULL" mit dem passenden Typ macht. Seufzer. – Shepmaster

+0

Danke, ich habe über eine Option nachgedacht, aber ich dachte, es könnte eine bessere Alternative geben, da nur das letzte Element 0 sein sollte. Ich denke, es gibt noch keine. – jck

Verwandte Themen