2015-07-10 10 views
5

Próbuję uczyć Rusta (początkującego programistę niskiego poziomu) i chcę przetłumaczyć małą wtyczkę wzmacniacza lv2 (audio) "amp.c" (C-code) od C do Rusta . Właściwie to działa (here), ale kiedy host kończy, valgrind mówi, że " 64 bajty w 1 blokach są zdecydowanie stracone". Myślę, że wiem, dlaczego tak się dzieje, ale nie wiem, jak to naprawić.statyczna struktura z ciągami C dla wtyczki lv2

Przed znudzi czytanie, tu jest ostatnie pytanie:

Jak statycznie przydziela struct, który zawiera ciąg C?

I tu jest wprowadzenie:

Dlaczego zdarza się (chyba): Host ładuje bibliotekę i wzywa lv2_descriptor()

const LV2_Descriptor* 
lv2_descriptor() 
{ 
    return &descriptor; 
} 

która zwraca wskaźnik do statycznie przydzielonego struktury typu LV2_Descriptor,

static const LV2_Descriptor descriptor = { 
    AMP_URI, 
    ... 
}; 

, który jest zdefiniowany jako

typedef struct _LV2_Descriptor { 
    const char * URI; 
    ... 
} LV2_Descriptor; 

Dlaczego jest statycznie przydzielony? W amp.c mówi:

Najlepiej jest określić deskryptory statycznie, aby uniknąć wycieka pamięć i innych przenośnych bibliotek współdzielonych konstruktory i destruktory do czyszczenia się prawidłowo.

Jednak przetłumaczyłem lv2_descriptor(), aby Rust jako:

#[no_mangle] 
pub extern fn lv2_descriptor(index:i32) -> *const LV2Descriptor { 
    let s = "http://example.org/eg-amp_rust"; 
    let cstr = CString::new(s).unwrap(); 
    let ptr = cstr.as_ptr(); 
    mem::forget(cstr); 

    let mybox = Box::new(LV2Descriptor{amp_uri: ptr}, ...); 
    let bxptr = &*mybox as *const LV2Descriptor; 
    mem::forget(mybox); 
    return bxptr 
    } 

Więc to nie jest alokowany statycznie i nigdy go za darmo, to myślę, dlaczego valgrind narzeka?

Jak mogę to rozwiązać? Próbuję zrobić to samo w Rust, jak kod C, to znaczy statycznie przydzielić strukturę (poza z lv2_descriptor()). Celem jest być w pełni kompatybilny z biblioteką lv2, tj. "... aby uniknąć wycieku pamięci ..." itp., Jak to podano w cytacie, prawda? Więc próbowałem coś takiego:

static ptr1: *const u8 = (b"example.org/eg-amp_rust\n\0").as_ptr(); 
static ptr2: *const libc::c_char = ptr1 as *const libc::c_char; 
static desc: LV2Descriptor = LV2Descriptor{amp_uri: ptr2, ...}; 

Ale to nie skompilować, istnieją komunikaty o błędach, takich jak

src/lib.rs:184:26: 184:72 error: the trait `core::marker::Sync` is not implemented for the type `*const u8` [E0277] 
src/lib.rs:184 static ptr1: *const u8 = b"http://example.org/eg-amp_rust\n\0".as_ptr(); 
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
src/lib.rs:184:26: 184:72 note: `*const u8` cannot be shared between threads safely 
src/lib.rs:184 static ptr1: *const u8 = b"http://example.org/eg-amp_rust\n\0".as_ptr(); 
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
src/lib.rs:184:26: 184:72 error: static contains unimplemented expression type [E0019] 
src/lib.rs:184 static ptr1: *const u8 = b"http://example.org/eg-amp_rust\n\0".as_ptr(); 

specyficzny problem/pytanie:

Jak statycznie przydziela struct że zawiera ciąg C?

+2

Nowicjusz w programowaniu niskiego poziomu * kto używa valgrind, rozumie jego raport i śledzi wyciek pamięci? Podaję ci kapelusz, proszę pana! –

+0

@Shepmaster: Dzięki, nie widziałem tego, to rzeczywiście ten sam problem. Wypróbowałem odpowiedź Kera i działa idealnie, więc może możemy dostosować odpowiedź Kerera do pierwotnego pytania i usunąć to? – poidl

+0

@MatthieuM .: Cóż, musiałem napisać program Fortran ~ 1000 linii raz do pracy, i musiałem użyć valgrind tam ... Myślę, że w porównaniu do większości ludzi tutaj wciąż jestem całkiem początkującym :) – poidl

Odpowiedz

3

Krótka odpowiedź brzmi: nie na razie. Future Rust prawdopodobnie uzyska tę zdolność.

Co możesz zrobić, to statycznie przydzielić strukturę zawierającą wskaźniki zerowe i ustawić te wskaźniki zerowe na coś użytecznego, gdy wywołasz funkcję. Rdza ma static mut.Wymaga to niebezpiecznego kodu, jest nie w ogóle nicifioletowym i jest (według mojej najlepszej wiedzy) uważany za kod zapach.

W tym miejscu uważam, że jest to obejście tego, że nie można w żaden sposób zmienić &[T] w statyczny.

static S: &'static [u8] = b"http://example.org/eg-amp_rust\n\0"; 
static mut desc: LV2Descriptor = LV2Descriptor { 
    amp_uri: 0 as *const libc::c_char, // ptr::null() isn't const fn (yet) 
}; 

#[no_mangle] 
pub extern fn lv2_descriptor(index: i32) -> *const LV2Descriptor { 
    let ptr = S.as_ptr() as *const libc::c_char; 
    unsafe { 
     desc.amp_uri = ptr; 
     &desc as *const LV2Descriptor 
    } 
} 
+0

Doskonały, I wypróbowany i wiadomość valgrind zniknęła. Dzięki! Powinienem wskazać osobom zainteresowanym lv2, że w łańcuchu amp_uri musi być * no * newline '\ n'. Mój błąd. – poidl