Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Разобраться с плагинами в программе


Автор: vase 18.8.2008, 11:56
Написал программу, использующу плагины (код класса для загрузки плагина взят с какого-то сайта).
Все работает - проблем нет. Но вызов функции из класса работает только тогда, когда она объявлена была ранее как virtual в файле plugin.h класса PlugIn

Можно ли как-то обойтись без объявления функции или нет? Хочется иметь возможность загрузить из библиотеки любую функцию и получить результат...

Библиотека подгружается след образом:
Код

    DLLFactory<PlugInFactory> so("/home/vase/Projects/panel/src/server/backends/gentoo/services/services.so"); 
    PlugIn *c=so.factory->CreatePlugIn(); 
    c->Show(); 
    c->Test(); 
    delete c; 


Код

class PluginManager {
  public:
    PluginManager(const char *fname);
     virtual ~ PluginManager();


    bool GetFunction(void **, const char *sym_name);

    const char *LastError() {
        return err;
    }
  protected:
    void *h;
    const char *err;
};

class DLLFactoryBase:public PluginManager {
  public:
    DLLFactoryBase(const char *fname, const char *func_name = 0);
    virtual ~ DLLFactoryBase();
    void *(*factory_func) (void);
};

template < class T > class DLLFactory:public DLLFactoryBase {
  public:
  DLLFactory(const char *fname, const char *func_name = 0):DLLFactoryBase(fname, func_name)
    {
        if (factory_func)
            factory = (T *) factory_func();
        else
            factory = 0;
    }
    ~DLLFactory() {
        delete factory;
    }

    T *factory;
};


Код

#include <dlfcn.h> 
#include "modules.h" 

PluginManager::PluginManager( const char *fname ) {
        h=dlopen( fname, RTLD_NOW );
        err=dlerror();
}

PluginManager::~PluginManager() {
        if( h!=0 )
        dlclose(h);
}


bool PluginManager::GetFunction( void **v, const char *sym_name) {
        if( h!=0 ) {
            *v = dlsym( h, sym_name );
            err=dlerror();
            if( err==0 )  return true;
            else return false;
        } else {
            return false;
        }
}

DLLFactoryBase::DLLFactoryBase( const char *fname, const char *factory ) : PluginManager(fname) {
        factory_func=0;
        if( LastError()==0 ) {
            GetFunction( (void **)&factory_func, factory ? factory : "factory0" );
        }

}

DLLFactoryBase::~DLLFactoryBase() {

}


Код

#include <iostream> 
#include "plugin.h" 

PlugIn::PlugIn()
{
        std::cout << "PlugIn Created" << std::endl;
}

PlugIn::~PlugIn()
{
        std::cout << "PlugIn Destroyed" << std::endl;
}



Код

#include "modules.h" 

#ifndef __PLUGIN_H 
#define __PLUGIN_H 


class PlugIn {
  public:
    PlugIn();

    virtual ~ PlugIn();

    virtual void Show() = 0;
};


class PlugInFactory {
  public:
    PlugInFactory() {

    } virtual ~ PlugInFactory() {

    }

    virtual PlugIn *CreatePlugIn() = 0;

};


сам код плагина

Код

#include "services.h" 

Services::Services() {

}


void Services::Show() {
    std::cerr << "I'm service PlugIn!" << std::endl;
}

void Services::Test() {
    std::cerr << "I'm Test from service PlugIn!" << std::endl;
}

Services::~Services() {

}



Код

#include <iostream> 

#include "plugin.h" 


class Services:public PlugIn {

  public:
    Services();
    virtual ~ Services();
    virtual void Show();
    virtual void Test();

};

class PlugInFunFactory:public PlugInFactory {
  public:
    PlugInFunFactory() {

    } ~PlugInFunFactory() {

    }

    virtual PlugIn *CreatePlugIn() {
        return new Services;
    }

};

extern "C" void *factory0(void)
{
    return new PlugInFunFactory;
}


Автор: bsa 18.8.2008, 12:30
вообще-то я не очень понимаю, зачем тебе загружать методы класса? Класс как таковой бесполезен без объявления...
Если тебе требуется загружать из плагина объект некоего класса, то этот класс должен быть потомком метакласса, объявление которого известно основной программе, и виртуальные методы переопределены в плагине... Но тут возникает сложность с компиляторами, так как вряд ли будет работать плагин написаный на компиляторе Mingw с программой, созданной в VC...

Автор: vase 18.8.2008, 12:36
Цитата(bsa @ 18.8.2008,  12:30)
вообще-то я не очень понимаю, зачем тебе загружать методы класса? Класс как таковой бесполезен без объявления...
Если тебе требуется загружать из плагина объект некоего класса, то этот класс должен быть потомком метакласса, объявление которого известно основной программе, и виртуальные методы переопределены в плагине... Но тут возникает сложность с компиляторами, так как вряд ли будет работать плагин написаный на компиляторе Mingw с программой, созданной в VC...

То есть в плагин лучше пихать только функции и никаких классов?  Тогда не придется их объявлять ранее ?

Автор: bsa 18.8.2008, 12:46
Цитата(vase @ 18.8.2008,  12:36)
То есть в плагин лучше пихать только функции и никаких классов?  Тогда не придется их объявлять ранее ?

Ну... вообще-то придется, иначе как их ты будешь использовать?
Тут смысл такой, что из плагина ты получаешь несколько заранее известных функции, например: init, process и quit. Первая, допустим, возвращает некий указатель. process - вызывает обработку данные (ему передается указатель), а quit - освобождает память, которая была выделена при инициализации. А вот внутри process, init и quit ты можешь использовать все что угодно. И указатель может указывать на объект класса - из основной программы напрямую обращений к нему нет.

Автор: vase 18.8.2008, 13:03
Цитата(bsa @ 18.8.2008,  12:46)
Цитата(vase @ 18.8.2008,  12:36)
То есть в плагин лучше пихать только функции и никаких классов?  Тогда не придется их объявлять ранее ?

Ну... вообще-то придется, иначе как их ты будешь использовать?
Тут смысл такой, что из плагина ты получаешь несколько заранее известных функции, например: init, process и quit. Первая, допустим, возвращает некий указатель. process - вызывает обработку данные (ему передается указатель), а quit - освобождает память, которая была выделена при инициализации. А вот внутри process, init и quit ты можешь использовать все что угодно. И указатель может указывать на объект класса - из основной программы напрямую обращений к нему нет.

А как тогда делать если надо вызывать разные функции клиентом?
Например, у меня есть приложение которое может создавать и удалять пользователей, а также запускать и останавливать сервисы на сервере. Я клиентом передаю команду, например 
users delete petya
сервер получил команду, загружает библиотеку , которая отвечает за функции с пользователями (например users.so). Далее мне надо вызвать что-то типа process(char *command) , а уже в функции process у меня должен быть switch (command) ..... так ?

Автор: bsa 18.8.2008, 13:11
угу

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)