2016-03-16 99 views
8

dlopen()是一個C函數,用於在運行時動態加載共享庫。該模式,如果你不熟悉,是這樣的:std :: shared_ptr和dlopen(),避免未定義的行爲

  • 呼叫dlopen("libpath", flag)獲得void *handle圖書館
  • 呼叫dlsym(handle, "object_name")獲得void *object你從圖書館
  • 確實想要的東西你想要什麼object
  • 致電dlclose (handle)卸載庫。

這一點,在C++中,完美用例爲所謂的混疊構造std::shared_ptr。該模式變爲:

  • 構建std::shared_ptr<void> handledlopen("libpath", flag)將調用dlclose()時,其調用析構函數
  • 構建從handle一個std::shared_ptr<void> objectdlsym(handle, "object_name")
  • 現在我們可以通過object的地方,我們想要的,完全忘記handle;當object的析構函數被調用,每當出現這種情況是,dlclose()將被自動地稱爲

輝煌模式,它精美的作品。一個小問題,但。上述模式需要演員從void*whatever_type_object_is*。如果"object_name"引用一個函數(大多數情況下,考慮用例),這是未定義的行爲。

在C中,有一個黑客可以解決這個問題。從dlopen手冊頁:

// ... 
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"); 
// ... 

這顯然工作得很好,在C.但是,有一個簡單的方法與std::shared_ptr做到這一點?

+0

爲什麼你需要'std :: shared_ptr'給po poetter,由dlsym返回? – Slava

+1

@Slava:保證一生(當有指針時不要調用'dlclose')。 – Jarod42

+0

'std :: shared_ptr'的別名構造函數允許兩個'std :: shared_ptr'在沒有指向相同對象或甚至相同類型的情況下共享相同的「關閉條件」(我的術語,而非官方)。使用'std :: shared_ptr'作爲'dlsym()'返回的值可以帶來以下好處:庫的生命週期與'object'的生命週期相關聯。 – Arandur

回答

4

上面的模式需要從void *轉換爲whatever_type_object_is *。如果「object_name」引用一個函數(大多數時候它考慮用例),這是未定義的行爲。

這是不完全正確的,至少在C++中它是有條件支持的。

5.2.10.8說:

轉換函數指針到一個對象的指針類型或反之亦然有條件地支持。這種轉換的含義 是實現定義的,除非如果一個實現支持兩個方向的轉換,將一種類型的prvalue轉換爲另一種類型並返回,可能具有不同的cv資格, 應產生原始指針值。

那麼假設什麼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

你可以做一個結構有你的函數指針和處理圖書館:

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; 
}; 

我懷疑是否需要hack。正如@sbabbi指出,有條件地支持往返void*。在使用dlsym返回函數指針的系統上,最好支持。

+0

您不從'std :: enable_shared_from_this'繼承。我認爲你交換'shared_ptr'參數的順序(對於* aliasing *構造函數)。 – Jarod42

+0

@ Jarod42 oops,早期版本。原來存儲一個「句柄」更聰明。 – Yakk

相關問題