2011-01-22 7 views
12

Próbuję napisać C++ 0x otoki wokół dlopen()/dlsym(), aby dynamicznie ładować funkcji z udostępnionych obiektów:Przechowywanie wskaźnik funkcji w std :: funkcja

class DynamicLoader 
{ 
    public: 
    DynamicLoader(std::string const& filename); 

    template<class Signature> 
     std::function<Signature> load(std::string const& functionName); 

    private: 
    void *itsLibraryHandle; 
}; 


DynamicLoader::DynamicLoader(std::string const& filename) 
{ 
    itsLibraryHandle = dlopen(filename.c_str(), RTLD_LAZY); 

    if(!itsLibraryHandle) 
    { /* Throw Some Error */ } 
} 

    template<class Signature> 
std::function<Signature> DynamicLoader::load(std::string const& functionName) 
{ 
    return <insert magic here> dlsym(itsHandle, functionName.c_str()); 
} 

Czy istnieje sposób skonwertować wskaźnik funkcji void * zwrócony przez dlsym na funkcję std ::?

Odpowiedz

6

spróbuj tego:

static_cast<Signature*>() 

wydaje prace w VC10

pełne badanie:

#include <functional> 

void test() 
{} 

template <typename Signature> 
std::function<Signature> cast(void* f) 
{ 
    return static_cast<Signature*>(f); 
} 

int main() 
{ 
    std::function<void()> f = cast<void()>(&test); 
    return 0; 
} 
+0

Dlaczego owinąć 'static_cast' w funkcję? –

+0

@NiklasR: Nie pamiętam dokładnie, może po to, żeby pokazać, jak to zrobić w ogólny sposób –

+2

Musiałem użyć 'reinterpret_cast', aby skompilować to na Linuksie. – Tom

6

Na podstawie tego, co widzę tutaj: http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html

#include <boost/function_types/components.hpp> 
#include <boost/function_types/function_pointer.hpp> 

template< typename Signature > 
std::function<Signature> DynamicLoader::load(std::string const& name) 
{ 
    namespace ft = boost::function_types; 
    typedef typename ft::function_pointer< typename ft::components<Signature>::type >::type fp_t; 
    fp_t fun_ptr; 

    *reinterpret_cast<void**>(&fun_ptr) = dlsym(itsHandle, name.c_str()); 

    return fun_ptr; 
} 

nigdy nie używałem dlsym więc nie rozumiem dlaczego obsada jest robione w ten sposób, a nie po prostu rzucając zwrot dlsym jest tak:

fun_ptr = reinterpret_cast<fp_t>(dlsym(itsHandle, name.c_str()); 
+1

nie jestem pewien o '' static_cast' reinterpret_cast' vs. tutaj (przynajmniej w tym ostatnim przypadku, ale oprócz tego, że wygląda dobrze –

+0

/* Writing. Cosinus = (double (*) (double)) dlsym (uchwyt, "cos"); wydaje się bardziej naturalny, ale standard C99 pozostawia od rzucanie od "void *" do niezdefiniowanego wskaźnika funkcji. Przyporządkowanie użyte poniżej to POSIX. 1-2003 (Technical Corrigendum 1) obejście, patrz uzasadnienie dla specyfikacji dlsym(). */From dlopen manpage –

+0

Rozwiązanie to działa również na unixie, w przeciwieństwie do wersji VC10 – toting

2

Wystarczy rzucić wynik dlsym() zadzwonić do odpowiedniego typu. Oto pełna przykład praca:

#include <functional> 
#include <iostream> 
#include <stdexcept> 
#include <string> 
#include <dlfcn.h> 

class DynamicLoader 
{ 
public: 
    DynamicLoader(std::string const& filename) : 
     m_handle(dlopen(filename.c_str(), RTLD_LAZY)) 
    { 
     if (!m_handle) 
     { 
      throw std::logic_error("can't load library named \"" + filename + "\""); 
     } 
    } 

    template<class T> 
    std::function<T> load(std::string const& functionName) const 
    { 
     dlerror(); 
     void* const result = dlsym(m_handle, functionName.c_str()); 
     if (!result) 
     { 
      char* const error = dlerror(); 
      if (error) 
      { 
       throw std::logic_error("can't find symbol named \"" + functionName + "\": " + error); 
      } 
     } 

     return reinterpret_cast<T*>(result); 
    } 

private: 
    void* const m_handle; 
}; 

int main() 
{ 
    DynamicLoader const loader("/lib64/libz.so.1"); 
    auto const zlibVersion = loader.load<char const* (void)>("zlibVersion"); 
    std::cout << "zlib version: " << zlibVersion() << std::endl; 
    return 0; 
} 
+0

Sprawdzanie wyniku nie jest poprawnym sposobem sprawdzenia, czy 'dlsym' nie działa. Musisz sprawdzić zwracaną wartość 'dlerror', tak jak jest to napisane na stronie podręcznika. Dzieje się tak, ponieważ 'NULL' jest poprawną wartością symbolu. –

+0

@MattClarkson Dzięki, naprawione. –