2016-03-16 120 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

相关问题