2015-08-28 11 views
8

मैं सी एपीआई के एक टुकड़े के लिए एक जंगली रैपर लिखने की कोशिश कर रहा हूं। संख्या की श्रेणी के लिएमैं एक सी-स्टाइल कॉलबैक में जंग को बंद कैसे कर सकता हूं?

typedef bool (*listener_t) (int, int); 
bool do_it(int x1, int y1, int x2, int y2, listener_t listener) 

समारोह अपने काम करता है जब तक कि श्रोता झूठे रिटर्न: एक सी निर्माण मैं के साथ संघर्ष नहीं है। उस स्थिति में यह गणना गणना करता है। मैं इस तरह एक जंग आवरण करना चाहते हैं:

fn do_with_callback<F>(start: (i32, i32), end: (i32, i32), callback: F) 
    where F: Fn(i32, i32) -> bool 

rust-bindgen मेरे लिए यह बनाई गई, थोड़ा स्पष्टता के लिए संपादित:

pub type listener_t = Option<extern "C" fn(x: c_int, y: c_int) -> c_bool>; 

pub fn TCOD_line(xFrom: c_int, yFrom: c_int, 
       xTo: c_int, yTo: c_int, 
       listener: listener_t) -> c_bool; 

मैं एक बंद या एक सी शैली के लिए एक विशेषता संदर्भ में परिवर्तित करना चाहिए मेरी do_with कार्यों में कॉलबैक:

pub fn do_with_callback<F>(start: (i32, i32), end: (i32, i32), callback: F) -> Self 
    where F: Fn(i32, i32) -> bool 
{ 
    let wrapper = ???; 
    unsafe { 
     ffi::do_it(start.0, start.1, end.0, end.1, Some(wrapper)) 
    }; 
} 
+0

चले गए इस पुस्तकालय पास फोन करने वाले-विशिष्ट जानकारी की एक सी ग्राहक कैसे करता है? ऐसा लगता है कि एक एपीआई का उदाहरण है जो इसे अनुमति देने के लिए डिज़ाइन नहीं किया गया था। शायद एपीआई लेखकों का मानना ​​है कि इसकी आवश्यकता नहीं है, और आप '(x, y)' के आधार पर आवश्यक सभी निर्णय ले सकते हैं। – Shepmaster

+1

ठीक है, सी पुस्तकालय विशेष रूप से अच्छी तरह से डिजाइन नहीं है। यह 'स्थिर और वैश्विक राज्य पर बहुत निर्भर करता है। और यह थ्रेड-सुरक्षित होने का भी प्रयास नहीं करता है। – Tomo

उत्तर

14

आप यह नहीं कर सकते जब तक कि सी एपीआई एक उपयोगकर्ता द्वारा प्रदान की कॉलबैक पैरामीटर गुजर अनुमति देता है। यदि ऐसा नहीं होता है, तो आप केवल स्थिर कार्यों का उपयोग कर सकते हैं।

कारण यह है कि बंद "कार्य" नहीं होते हैं। जैसा कि उनके नाम का तात्पर्य है, उनके अक्षीय दायरे से "बंद" चर को बंद कर देता है। प्रत्येक बंद में डेटा का एक संबद्ध टुकड़ा होता है जिसमें या तो कैप्चर किए गए चर के मान होते हैं (यदि move कीवर्ड का उपयोग किया जाता है) या संदर्भ। इस डेटा को कुछ अज्ञात, अज्ञात struct के रूप में सोचा जा सकता है।

कंपाइलर स्वचालित रूप से इन अज्ञात structs के लिए संबंधित Fn* लक्षणों का कार्यान्वयन जोड़ता है। As you can see, इन लक्षणों पर विधियां बंद करने के तर्क के अलावा self स्वीकार करती हैं। इस संदर्भ में, selfstruct है जिस पर विशेषता लागू की गई है। इसका मतलब यह है कि प्रत्येक कार्य जो बंद होने के अनुरूप होता है, में एक अतिरिक्त पैरामीटर भी होता है जिसमें बंद वातावरण होता है।

यदि आपका सी एपीआई केवल आपको उपयोगकर्ता द्वारा परिभाषित पैरामीटर के बिना फ़ंक्शन पास करने की अनुमति देता है, तो आप एक रैपर नहीं लिख सकते हैं जो आपको बंद करने का उपयोग करने की अनुमति देगा। मुझे लगता है कि बंद पर्यावरण के लिए कुछ वैश्विक धारक लिखना संभव है, लेकिन मुझे संदेह है कि यह आसान और सुरक्षित होगा।

extern crate libc; 

use std::mem; 

use libc::{c_int, c_void}; 

extern "C" { 
    fn do_something(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void) -> c_int; 
} 

extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int { 
    let closure: &mut &mut FnMut(i32) -> bool = unsafe { mem::transmute(arg) }; 
    closure(x as i32) as c_int 
} 

pub fn do_with_callback<F>(x: i32, mut callback: F) -> bool 
    where F: FnMut(i32) -> bool 
{ 
    let cb: &mut FnMut(i32) -> bool = &mut callback; 
    unsafe { do_something(Some(do_something_handler), cb as *mut _ as *mut c_void) > 0 } 
} 

यह तभी do_something सूचक की दुकान नहीं है काम करेंगे:

अपने सी एपीआई उपयोगकर्ता परिभाषित तर्क गुजर अनुमति नहीं है, तो यह है कि तुम क्या विशेषता के साथ चाहते हैं वस्तुओं करना संभव है कहीं कॉलबैक यदि ऐसा होता है, तो आपको Box<Fn(..) -> ..> विशेषता ऑब्जेक्ट का उपयोग करने की आवश्यकता है और इसे फ़ंक्शन पर पास करने के बाद इसे रिसाव करें। फिर, यदि संभव हो, तो इसे आपकी सी लाइब्रेरी से वापस प्राप्त किया जाना चाहिए और इसका निपटारा किया जाना चाहिए। यह ऐसा दिखाई दे सकता:

extern crate libc; 

use std::mem; 

use libc::{c_int, c_void}; 

extern "C" { 
    fn set_handler(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void); 
    fn invoke_handler(x: c_int) -> c_int; 
    fn unset_handler() -> *mut c_void; 
} 

extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int { 
    let closure: &mut Box<FnMut(i32) -> bool> = unsafe { mem::transmute(arg) }; 
    closure(x as i32) as c_int 
} 

pub fn set_callback<F>(callback: F) 
    where F: FnMut(i32) -> bool, 
      F: 'static 
{ 
    let cb: Box<Box<FnMut(i32) -> bool>> = Box::new(Box::new(callback)); 
    unsafe { 
     set_handler(Some(do_something_handler), Box::into_raw(cb) as *mut _); 
    } 
} 

pub fn invoke_callback(x: i32) -> bool { 
    unsafe { invoke_handler(x as c_int) > 0 } 
} 

pub fn unset_callback() { 
    let ptr = unsafe { unset_handler() }; 
    // drop the callback 
    let _: Box<Box<FnMut(i32) -> bool>> = unsafe { Box::from_raw(ptr as *mut _) }; 
} 

fn main() { 
    let mut y = 0; 
    set_callback(move |x| { 
     y += 1; 
     x > y 
    }); 

    println!("First: {}", invoke_callback(2)); 
    println!("Second: {}", invoke_callback(2)); 

    unset_callback(); 
} 

डबल अविवेक (अर्थात Box<Box<...>>) आवश्यक है, क्योंकि Box<Fn(..) -> ..> एक विशेषता वस्तु और इसलिए एक वसा सूचक, विभिन्न आकार की वजह से *mut c_void साथ असंगत है।

+0

मैं देखता हूं। दुर्भाग्यवश यह विशेष फ़ंक्शन किसी भी उपयोगकर्ता डेटा को पास करने की अनुमति नहीं देता है। तो, इसे काम करने के लिए मुझे उपयोगकर्ताओं को 'बाहरी "सी" 'फ़ंक्शन को मेरे जंग के आवरण के पैरामीटर के रूप में प्रदान करने की आवश्यकता होगी? या एक विशेषता/वस्तु का उपयोग करने का कोई तरीका है? – Tomo

+0

दुर्भाग्यवश, मुझे 'बाहरी "सी" फ़ंक्शन को पार करने से कोई अन्य तरीका नहीं दिखता है। शायद कोई और कुछ सुझाव दे सकता है, लेकिन यह असंभव है। –

+0

@VladimirMatveev मुझे एक समान आवश्यकता है। मेरे मामले में userdata को कॉलबैक में पास कर दिया गया है, लेकिन यह इस तरह के फ़ंक्शन का उपयोग करके सेट किया गया है -> 'बाइंडिंग :: mosquitto_user_data_set'। इसके कारण मेरा बंद होने का समय कॉलबैक द्वारा नष्ट हो रहा है और मुझे बंद चर पर कचरा मिल रहा है। क्या आप उत्तर देने वाले 'बॉक्स' विधि के उदाहरण के साथ उत्तर का विस्तार कर सकते हैं? – tez

1

The first snippet from Vladimir Matveev अब लिखित रूप में काम नहीं करता है। &mut FnMut(i32) -> bool और *mut c_void का आकार अलग है और ऐसे कलाकारों को एक दुर्घटना का कारण बनता है।सही उदाहरण (playpen):

extern crate libc; 

use std::mem::*; 

use libc::c_void; 

pub fn run<F>(mut callback: F) -> bool 
    where F: FnMut(i32) -> bool 
{ 
    let mut cb: &mut FnMut(i32) -> bool = &mut callback; 
    println!("sizeof(cb/*-ptr): {}/{}", 
      size_of::<*mut FnMut(i32) -> bool>(), 
      size_of::<*mut c_void>()); 

    let ctx = &mut cb as *mut &mut FnMut(i32) -> bool as *mut c_void; 
    println!("ctx: {:?}", ctx); 
    //---------------------------------------------------------- 
    // Convert backward 
    let cb2: *mut *mut FnMut(i32) -> bool = unsafe { transmute(ctx) }; 
    println!("cb2: {:?}", cb2); 

    // this is more useful, but can't be printed, because not implement Debug 
    let closure: &mut &mut FnMut(i32) -> bool = unsafe { transmute(ctx) }; 

    closure(0xDEAD) 
} 

fn main() { 
    println!("answer: {}", 
      run(|x| { 
       println!("What can change nature of a man?"); 
       x > 42 
      })); 
} 
+0

अन्य उत्तर में दोनों कोड स्निपेट सही ढंग से संकलित करते हैं। क्या आप कह रहे हैं कि प्रोग्राम निष्पादित होने पर एक क्रैश है? – Shepmaster

+0

हां। बस play.rust-lang.org में इसे आज़माएं। यह साइट क्रैश की रिपोर्ट नहीं करती है, बस कुछ 'println'ed स्ट्रिंग मुद्रित नहीं हैं, जो मुझे बताती हैं, वह एप्लिकेशन क्रैश हो जाता है। पहले से ही एक तथ्य यह है कि सृजन सूचक के दौरान अलग-अलग मात्रा 'और म्यूट' का उपयोग किया जाता है और सूचकांक को बंद करने के लिए बंद होने के बाद। – Mingun

2

सी में एक समारोह सूचक संबद्ध कर दिया है नहीं करता संदर्भ है, जिसके कारण आम तौर पर एक सी कॉलबैक फ़ंक्शन आमतौर पर ले जाने के लिए एक अतिरिक्त void* तर्क संदर्भ पारित ...

typedef bool (*listener_t)(int, int, void* user_data); 
bool do_it(void* user_data, int x1, int y1, int x2, int y2, listener_t listener) 

... या उपयोगकर्ता डाटा स्टोर करने जाने के लिए एक API है ...

void api_set_user_data(void* user_data); // <-- caller set the context 
void* api_get_user_data(); // <-- callback use this to retrieve context. 

यदि आप जिस पुस्तकालय को लपेटना चाहते हैं, वह उपरोक्त में से कोई भी प्रदान नहीं करता है, तो आपको अन्य चैनलों के माध्यम से संदर्भ पारित करने की आवश्यकता होगी, उदा। एक वैश्विक चर के माध्यम से है, हालांकि इस संदर्भ पूरी प्रक्रिया में साझा किया जाएगा:

lazy_static! { 
    static ref REAL_CALLBACK: Mutex<Option<Box<FnMut(c_int, c_int) -> bool + Send>>> = Default::default(); 
} 

extern "C" fn callback(x: c_int, y: c_int) -> bool { 
    if let Some(ref mut real_callback) = *REAL_CALLBACK.lock().unwrap() { 
     real_callback(x, y) 
    } else { 
     panic!("<handle error here>"); 
    } 
} 

fn main() { 
    *REAL_CALLBACK.lock().unwrap() = Some(Box::new(move |x, y| { 
     println!("..."); 
     true 
    })); 
    unsafe { 
     do_it(callback); 
    } 
} 

यह भी संभव एक trampoline function समारोह में सीधे संदर्भ रहना बनाने के लिए है, लेकिन यह बहुत ही मुश्किल और असुरक्षित है।

उत्तर मैन्युअल रूप से https://stackoverflow.com/a/42597209/224671

संबंधित मुद्दे