Съхраняване на указател на функция в std::function

Опитвам се да напиша C++0x обвивка около dlopen()/dlsym() за динамично зареждане на функции от споделени обекти:

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

Има ли начин да преобразувате указателя на функцията void*, върнат от dlsym, в std::function?


person rcv    schedule 22.01.2011    source източник


Отговори (3)


Опитайте тази:

static_cast<Signature*>()

изглежда работи във VC10

пълен тест:

#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;
}
person Andriy Tylychko    schedule 22.01.2011
comment
Защо обвихте static_cast във функция? - person Niklas R; 01.12.2015
comment
@NiklasR: Не си спомням точно, може би само за да демонстрирам как се прави това по общ начин - person Andriy Tylychko; 02.12.2015
comment
Трябваше да използвам reinterpret_cast, за да накарам това да се компилира на Linux. - person Tom; 19.01.2017
comment
Не разбирам защо този отговор е приет. static_cast в този конкретен случай е само разширение на Microsoft. Опитайте се да изградите това във всеки UNIX/Linux компилатор или просто в clang. Ето го: godbolt.org/z/VuWu5M - person IGR94; 21.11.2019

Въз основа на това, което виждам тук: 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;
}

Никога не съм използвал dlsym, така че не разбирам защо прехвърлянето се извършва по този начин, а не просто предаване на връщането на dlsym така:

fun_ptr = reinterpret_cast<fp_t>(dlsym(itsHandle, name.c_str());
person Edward Strange    schedule 22.01.2011
comment
Не съм сигурен за reinterpret_cast срещу static_cast тук (поне във втория случай, но освен това изглежда добре. - person Konrad Rudolph; 23.01.2011
comment
/* Писане: косинус = (double (*)(double)) dlsym(handle, cos); би изглеждало по-естествено, но стандартът C99 оставя кастинга от void * към указател на функция недефиниран. Присвояването, използвано по-долу, е заобиколно решение на POSIX.1-2003 (Техническа поправка 1); вижте Обосновката за POSIX спецификацията на dlsym(). */ От dlopen manpage - person Matt Clarkson; 17.12.2012
comment
Това решение работи и на unix, за разлика от VC10. - person toting; 03.02.2017

Просто трябва да приведете резултата от извикването на dlsym() към подходящ тип. Ето пълен работещ пример:

#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;
}
person mike.dld    schedule 22.01.2011
comment
Проверката на !result не е правилният начин да проверите дали dlsym е неуспешно. Трябва да проверите върнатата стойност на dlerror, както се казва в страницата за ръководство. Това е така, защото NULL е валидна символна стойност. - person Matt Clarkson; 17.12.2012
comment
@MattClarkson Благодаря, коригирано. - person mike.dld; 23.02.2014