не удалось успешно вызвать функцию в динамически загружаемом плагине в С++

Я успешно загрузил плагин C++, используя собственный класс загрузчика плагинов. Каждый плагин имеет внешнюю функцию create_instance "C", которая возвращает новый экземпляр, используя "new".

Плагин — это абстрактный класс с несколькими невиртуальными функциями и несколькими защищенными переменными (одной из них является std::vector refList).

Класс plugin_loader успешно загружает и даже вызывает виртуальный метод загруженного класса (а именно «std::string plugin::getName()».

Основная функция создает экземпляр «host», который содержит вектор интеллектуальных указателей с подсчетом ссылок, refptr, на класс «plugin». Затем main создает экземпляр plugin_loader, который фактически выполняет dlopen/dlsym, и создает экземпляр refptr, передавая ему функцию create_instance(). Наконец, он передает созданный refptr обратно функции addPlugin хоста. host::addPlugin успешно вызывает несколько функций для переданного экземпляра плагина и, наконец, добавляет его в плагин vector‹refptr‹› ›.

Затем основная функция подписывается на несколько событий Apple и вызывает RunApplicationEventLoop(). Обратный вызов события декодирует результат, а затем вызывает функцию хоста host::sendToPlugin, которая идентифицирует подключаемый модуль, для которого предназначено событие, а затем вызывает обработчик в подключаемом модуле. Именно в этот момент что-то перестает работать.

host::sendToPlugin считывает результат и определяет плагин для отправки события.

Я использую чрезвычайно простой плагин, созданный как плагин отладки, который возвращает статические значения для каждой непустой функции.

Любой вызов любой виртуальной функции в плагине в векторе вызывает исключение неправильного доступа. Я попытался заменить refptrs обычными указателями, а также boost::shared_ptrs, и я продолжаю получать одно и то же исключение. Я знаю, что экземпляр плагина действителен, так как я могу проверить экземпляр в отладчике Xcode и даже просмотреть элементы в refList плагина.

Я думаю, что это может быть проблема с потоками, потому что плагины были созданы в основном потоке, в то время как обратный вызов работает в отдельном потоке. Я думаю, что-то все еще работает в основном потоке поток, судя по обратной трассировке, когда программа обнаруживает ошибку, но я не знаю реализацию Apple RunApplicationEventLoop, поэтому я не могу быть уверен.

Любые идеи относительно того, почему это происходит?

class plugin
{
public:
    virtual std::string getName(); 

protected:
    std::vector<std::string> refList;
};

и класс pluginLoader:

template<typename T> class pluginLoader 
{
    public: pluginLoader(std::string path);
    // initializes private mPath string with path to dylib

    bool open(); 
    // opens the dylib and looks up the createInstance function. Returns true if successful, false otherwise

    T * create_instance(); 
    // Returns a new instance of T, NULL if unsuccessful
}; 

class host
{
public:
      addPlugin(int id, plugin * plug);
      sendToPlugin(); // this is the problem method
      static host * me;

private:
     std::vector<plugin *> plugins; // or vector<shared_ptr<plugin> > or vector<refptr<plugin> >
};

код события Apple из host.cpp;

host * host::me;
pascal OSErr HandleSpeechDoneAppleEvent(const AppleEvent *theAEevt, AppleEvent *reply, SRefCon refcon) {
         // this is all boilerplate taken straight from an apple sample except for the host::me->ae_callback line
         OSErr status = 0;
         Result result = 0;
         // get the result
         if (!status) {
             host::me->ae_callback(result);
         }
         return status;
}
void host::ae_callback(Result result) {
       OSErr err;
       // again, boilerplate apple code
   // grab information from result
       if (!err)
         sendToPlugin();
}
void host::sendToPlugin() {
       // calling *any* method in plugin results in failure regardless of what I do
}

РЕДАКТИРОВАТЬ: это выполняется на OSX 10.5.8, и я использую GCC 4.0 с Xcode. Это не приложение для разных платформ.

РЕДАКТИРОВАТЬ: Чтобы было ясно, плагин работает до тех пор, пока цикл обработки событий Apple не вызовет мою функцию обратного вызова. Когда функция обратного вызова обращается к хосту, все перестает работать. Это проблема, которая у меня возникла, все остальное до этого момента работало.


person user150113    schedule 16.01.2010    source источник
comment
Некоторый фрагмент кода поможет.   -  person Nikolai Fetissov    schedule 16.01.2010
comment
Название ОС тоже было бы удобно. Поскольку упоминается яблоко, я ПРЕДПОЛАГАЮ, что это последняя версия Mac OS.   -  person Jason D    schedule 16.01.2010
comment
это это os x, 10.5. Я думаю, что это более общее, чем то, что включает обратный вызов события. Что касается фрагмента кода, plugins[id]->getName();. На самом деле не имеет значения, какой метод я вызываю, я знаю, что цель существует, и я имею возможность вызывать для нее методы в основном потоке.   -  person user150113    schedule 16.01.2010


Ответы (2)


Не видя всего вашего кода, будет непросто понять, что именно идет не так. Некоторые вещи, на которые стоит обратить внимание:

  • Убедитесь, что компоновщик ничего не выбрасывает. В gcc попробуйте параметры компиляции -Wl -E - мы используем это в Linux, но, похоже, не нашли в этом необходимости на Mac.
  • Убедитесь, что вы случайно не выгружаете динамическую библиотеку до того, как закончите с ней работать. RAII не работает для выгрузки динамических библиотек, если вы также не останавливаете исключения на границе динамической библиотеки.

Вы можете изучить нашу библиотеку подключаемых модулей, которая работает на Linux, Mac и Windows. Код динамической загрузки (вместе с загрузкой других библиотечных материалов) доступен по адресу http://svn.felspar.com/public/fost-base/trunk/

Мы не используем механизм dlsym — его сложно использовать правильно (и переносимо). Вместо этого мы создаем библиотеку плагинов по имени и помещаем туда то, что по сути является фабрикой. Вы можете изучить, как это работает, посмотрев, как файлы .so с наборами тестов могут быть динамически загружены. Пример загрузчика находится по адресу http://svn.felspar.com/public/fost-base/trunk/fost-base/Cpp/fost-ftest/ftest.cpp, а регистрация набора тестов находится в http://svn.felspar.com/public/fost-base/trunk/fost-base/Cpp/fost-test/testsuite.cpp Threadsafe_store содержит фабрики по имени, а конструктор пакета регистрирует фабрику.

person KayEss    schedule 16.01.2010
comment
На самом деле я не выгружаю его, так как плагины остаются активными в течение всего срока службы приложения прямо сейчас. Я использую заводской шаблон, и он работает, на основном потоке все работает идеально. Как только основной поток запускает RunApplicationEventLoop и событие начинает обрабатываться в отдельном потоке, все перестает работать. Я отредактировал вопрос, добавив немного больше кода, связанного с событиями Apple, возможно, это поможет. - person user150113; 16.01.2010

Я полностью пропустил тот факт, что я вызывал dlclose в dtor моего plugin_loader, и по какой-то причине плагины разрушались между вызовом RunApplicatoinEventLoop и вызовом sendToPlugin. Я удалил dlclose, и теперь все работает.

person user150113    schedule 16.01.2010
comment
Если вы посмотрите на реализацию, которая у нас есть, вы увидите дополнительную реализацию fostlib::atexit(), которая позволяет планировать выполнение dlclose при завершении работы приложения. Таким образом, все по-прежнему уничтожается чисто. Вы можете увидеть код по адресу svn.felspar.com/public/fost-base/trunk/fost-base/Cpp/fost-core/ -- все, что он делает, это откладывает удаление внутреннего указателя pimpl до завершения работы приложения ( имя файла подразумевает, что это Linux, но тот же код используется и на Mac). - person KayEss; 17.01.2010
comment
Кстати, разве это не второй момент, который я сделал в своем ответе? Убедитесь, что вы случайно не выгружаете динамическую библиотеку до того, как закончите с ней работать. - person KayEss; 26.01.2010