2011-01-22 13 views
12

Sto cercando di scrivere un C++ 0x wrapper dlopen()/dlsym() per caricare dinamicamente le funzioni da oggetti condivisi:Memorizzazione puntatore a funzione in std :: funzione

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

Esiste un modo convertire il puntatore di funzione void * restituito da dlsym in una funzione std ::?

risposta

6

provare questo:

static_cast<Signature*>() 

sembra opere in VC10

test completo:

#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

Perché hai avvolgere 'static_cast' in una funzione? –

+0

@NiklasR: Non ricordo esattamente, forse solo per dimostrare come farlo in modo generico –

+2

Ho dovuto usare 'reinterpret_cast' per ottenere la compilazione su Linux. – Tom

6

In base a quello che vedo qui: 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; 
} 

non ho mai usato dlsym quindi non capisco perché il cast è stato fatto in quel modo e non semplicemente gettando il ritorno di dlsym in questo modo:

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

io non sono sicuro di '' reinterpret_cast' vs. static_cast' qui (almeno in quest'ultimo caso, ma a parte che questo sembra buono –

+0

/* scrittura:. Coseno = (double (*) (double)) dlsym (handle, "cos"); sembrerebbe più naturale, ma lo standard C99 lascia il lancio di da "void *" a un puntatore di funzione non definito. L'assegnazione utilizzata di seguito è POSIX. 1-2003 (Technical Corrigendum 1) soluzione alternativa, vedere la motivazione per la specifica POSIX di dlsym(). */Dalla manpage dlopen –

+0

Questa soluzione funziona anche su Unix, a differenza di VC10 – toting

2

Hai solo bisogno di risultato di dlsym pressofuso() chiamata a un tipo corretto. Ecco un esempio di lavoro completo:

#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

Il controllo! Risultato non è il modo corretto per controllare se 'dlsym' non è riuscito. È necessario controllare il valore di ritorno di 'dlerror' come indicato nella pagina man. Questo perché 'NULL' è un valore simbolo valido. –

+0

@MattClarkson Grazie, risolto. –

Problemi correlati