2016-03-16 11 views
8

dlopen() एक सी फ़ंक्शन है जो रनटाइम पर साझा लाइब्रेरी को गतिशील रूप से लोड करने के लिए उपयोग किया जाता है। पैटर्न, मामले में आप परिचित नहीं हैं, इस प्रकार है:std :: shared_ptr और dlopen(), अपरिभाषित व्यवहार से बचने

  • कॉल dlopen("libpath", flag) बात आप पुस्तकालय
  • Do से चाहते हैं के लिए एक void *object प्राप्त करने के लिए पुस्तकालय
  • कॉल dlsym(handle, "object_name") करने के लिए एक void *handle पाने के लिए आप object
  • लाइब्रेरी को अनलोड करने के लिए dlclose (handle) पर कॉल करें।

यह सी ++, तथाकथित अलियासिंग निर्माता std::shared_ptr की के लिए एक आदर्श यूज-केस में, है। पैटर्न हो जाता है:

  • का निर्माण dlopen("libpath", flag) से एक std::shared_ptr<void> handle कि dlclose() फोन करेगा जब उसके नाशक कहा जाता है
  • handle से एक std::shared_ptr<void> object और dlsym(handle, "object_name")
  • का निर्माण अब हम object पारित कर सकते हैं जहाँ भी हम चाहते हैं, और पूरी तरह से के बारे में भूल handle; जब object के नाशक कहा जाता है, जब भी उस होता है, dlclose() स्वतः

शानदार पैटर्न के नाम से जाना जाएगा, और यह खूबसूरती से काम करता है। हालांकि, एक छोटी सी समस्या है। उपरोक्त पैटर्न को void* से whatever_type_object_is* तक कास्ट की आवश्यकता है। यदि "object_name" किसी फ़ंक्शन को संदर्भित करता है (जो उपयोग-मामले पर विचार करते समय अधिकांश समय करता है), यह अनिर्धारित व्यवहार है।

सी में, इसके आसपास जाने के लिए एक हैक है।

// ... 
void *handle;  
double (*cosine)(double); 
// ... 
handle = dlopen("libm.so", RTLD_LAZY); 
// ... 

/* Writing: cosine = double (*)(double)) dlsym(handle, "cos"); 
    would seem more natural, but the C99 standard leaves 
    casting from "void *" to a function pointer undefined. 
    The assignment used below is the POSIX.1-2003 (Technical 
    Corrigendum 1) workaround; see the Rationale for the 
    POSIX specification of dlsym(). */ 

*(void **) (&cosine) = dlsym(handle, "cos"); 
// ... 

जो स्पष्ट रूप से बस ठीक काम करता है, सी में लेकिन वहाँ एक आसान तरीका std::shared_ptr के साथ ऐसा करना है: dlopen आदमी पृष्ठ से?

+0

आपको dlsym द्वारा लौटाए गए पॉइंटर में 'std :: shared_ptr' क्यों चाहिए? – Slava

+1

@ स्लावा: जीवनकाल की गारंटी के लिए (उस पॉइंटर होने पर 'dlclose' को कॉल न करें)। – Jarod42

+0

'std :: shared_ptr' का एलियासिंग कन्स्ट्रक्टर दो' std :: shared_ptr' को एक ही ऑब्जेक्ट या यहां तक ​​कि उसी प्रकार की ओर इशारा किए बिना एक ही "करीबी स्थिति" (मेरा शब्द, आधिकारिक नहीं) साझा करने की अनुमति देता है। 'Dlsym()' द्वारा दिए गए मान के लिए 'std :: shared_ptr' का उपयोग करके मुझे यह लाभ प्राप्त होता है: लाइब्रेरी का जीवनकाल 'ऑब्जेक्ट' के जीवनकाल से जुड़ा हुआ है। – Arandur

उत्तर

4

उपरोक्त पैटर्न को शून्य * से जो भी_type_object_is * तक कास्ट की आवश्यकता है। यदि "object_name" किसी फ़ंक्शन को संदर्भित करता है (जो अधिकांश समय उपयोग करता है, तो उपयोग-मामले पर विचार करता है), यह अपरिभाषित व्यवहार है।

वैसे यह पूरी तरह से सच नहीं है, कम से कम सी ++ में यह सशर्त रूप से समर्थित है।

5.2.10.8 का कहना है:

एक वस्तु सूचक प्रकार या ठीक इसके विपरीत करने के लिए एक समारोह सूचक परिवर्तित सशर्त-समर्थित है।इस तरह के रूपांतरण का अर्थ कार्यान्वयन-परिभाषित है, सिवाय इसके कि यदि कोई कार्यान्वयन दोनों दिशाओं में रूपांतरण का समर्थन करता है, तो एक प्रकार के प्रकोप को दूसरे प्रकार और पीछे के रूप में परिवर्तित करना, संभावित रूप से विभिन्न cvqualification के साथ, मूल सूचक मूल्य प्रदान करेगा।

तो यह सोचते हैं कि क्या dlsym करता आंतरिक रूप से एक void* करने के लिए एक समारोह सूचक कास्टिंग है, मुझे विश्वास है कि तुम ठीक हो अगर आप बस इसे एक समारोह सूचक के लिए वापस डाली।

+0

http://en.cppreference.com/w/cpp/language/reinterpret_cast विशेष बिंदु 8 में यह – Niall

+1

शामिल है और मुझे लगता है कि यह इरादा था http://www.open-std.org/jtc1/sc22/wg21 /docs/cwg_defects.html#195 – Niall

0

आप एक struct अपने सूचक काम करते हैं और पुस्तकालय के लिए संभाल करने के लिए कर सकते हैं:

template<typename T> 
struct dlsymbol { 
    dlsymbol(const std::string &name, std::shared_ptr<void> handle) : 
     m_handle(std::move(handle)) 
    { 
     *(void **)(&m_func) = dlsym(handle.get(), name.c_str); 
    } 

    std::shared_ptr<void> m_handle; 
    T *m_func; 
}; 

auto cosine = std::make_shared<dlsymbol<double(double)>>("cos", handle); 
auto d = cosine->m_func(1.0); 

मैं इसे संकलन नहीं था, लेकिन मुझे लगता है कि विचार को दिखाने के लिए पर्याप्त है।

1

ऐसा कुछ?

struct dlib 
{ 
public: 
    template<class T> 
    std::shared_ptr<T> sym(const char* name) const { 
    if (!handle) return {}; 
    void* sym = dlsym(handle->get(), name); 
    if (!sym) return {}; 
    return {reinterpret_cast<T*>(sym), handle}; 
    } 
    // returns a smart pointer pointing at a function for name: 
    template<class Sig> 
    std::shared_ptr<Sig*> pfunc(const char* name) const { 
    if (!handle) return {}; 
    void* sym = dlsym(handle->get(), name); 
    if (!sym) return {}; 
    Sig* ret = 0; 
    // apparently approved hack to convert void* to function pointer 
    // in some silly compilers: 
    *reinterpret_cast<void**>(&ret) = sym; 
    return {ret, handle}; 
    } 
    // returns a std::function<Sig> for a name: 
    template<class Sig> 
    std::function<Sig> function(const char* name) const { 
    // shared pointer to a function pointer: 
    auto pf = pfunc(name); 
    if (!pf) return {}; 
    return [pf=std::move(pf)](auto&&...args)->decltype(auto){ 
     return (*pf)(decltype(args)(args)...); 
    }; 
    } 
    dlib() = default; 
    dlib(dlib const&)=default; 
    dlib(dlib &&)=default; 
    dlib& operator=(dlib const&)=default; 
    dlib& operator=(dlib &&)=default; 

    dlib(const char* name, int flag) { 
    void* h = dlopen(name, flag); 
    if (h) 
    { 
     // set handle to cleanup the dlopen: 
     handle=std::shared_ptr<void>(
     h, 
     [](void* handle){ 
      int r = dlclose(handle); 
      ASSERT(r==0); 
     } 
    ); 
    } 
    } 
    explicit operator bool() const { return (bool)handle; } 
private: 
    std::shared_ptr<void> handle; 
}; 

मुझे संदेह है कि हैक की आवश्यकता है। जैसा कि @ सब्बी ने नोट किया, void* के लिए राउंड-ट्रिप सशर्त रूप से समर्थित है। फ़ंक्शन पॉइंटर्स को वापस करने के लिए dlsym का उपयोग कर सिस्टम पर, यह बेहतर समर्थित होगा।

+0

आपको 'std :: enable_shared_from_this' से प्राप्त नहीं है। मुझे लगता है कि आप 'shared_ptr' तर्कों के क्रम को स्वैप करते हैं (* एलियासिंग * कन्स्ट्रक्टर के लिए)। – Jarod42

+0

@ जारोड 42 ओओएस, पहले संस्करण ने किया था। 'हैंडल' संग्रहित करना स्मार्ट था। – Yakk

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