C ++在Linux平台上加载共享库并在运行时提取类实现

时间:2014-04-01 19:24:22

标签: c++ linux qt qt4 shared-libraries

在C ++中,是否可以在执行时加载共享库?

我希望用户选择在运行时加载哪个共享库,而无需重新编译整个程序。

dlopen()是C的解决方案,但是我的程序是用C ++ / Qt编写的,要提取的符号是Qt样式的类,有没有更“c ++”的方法来做到这一点。

3 个答案:

答案 0 :(得分:5)

您可以使用QLibrary以两种方式在Qt中执行此操作。以下示例在运行时以两种不同的方式从共享库调用函数:

#include <QLibrary>
#include <QDebug>

class  Dynamic_library
{
public:
    Dynamic_library();
    virtual int sum( int len, int * data );
};

typedef Dynamic_library * (*get_object_func)();
typedef int (*call_sum_func)(int len , int * data);

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QLibrary library( "./dynamic_library" );
    library.load();
    if( !library.isLoaded() )
    {
        qDebug() << "Cannot load library.";
        return 0;
    }
    call_sum_func call_sum = (call_sum_func)library.resolve( "call_sum" );
    if( call_sum )
    {
        //Dynamic_library * obj=get_object();

        int * a=new int[3];
        a[0]=2;
        a[1]=3;
        a[2]=4;
        qDebug() << "sum of 2+3+4' = " << call_sum( 3, a ) <<"\n";

        delete [] a;
    }

    get_object_func get_object = (get_object_func)library.resolve( "get_object" );
    if( get_object )
    {
        Dynamic_library * obj=get_object();

        int * a=new int[3];
        a[0]=7;
        a[1]=8;
        a[2]=9;
        qDebug() << "sum of 7+8+9' = " << obj->sum(3, a );

        delete [] a;
    }

    return a.exec();
}

共享库的代码如下:

class DYNAMIC_LIBRARYSHARED_EXPORT Dynamic_library
{
public:
    Dynamic_library();
    virtual int sum( int len, int * data );
};

extern "C" Q_DECL_EXPORT Dynamic_library * get_object()
{
     return new Dynamic_library();
}

extern "C" Q_DECL_EXPORT int call_sum(int len, int * data)
{
     return Dynamic_library().sum(len,data);
}


Dynamic_library::Dynamic_library()
{

}

int Dynamic_library::sum( int len, int *data )
{
    int sum = 0;
    for(int i=0; i<len; ++i )
        sum += data[i];

    return sum;
}

答案 1 :(得分:2)

如果目标库本身或至少其规格在您的控制之下,那么您不应该使用QLibrary - 而是使用Qt plugin system。它不需要另外需要的呼叫指针体操。

如果您坚持使用类似dlopen的机制,那么QLibrary没有任何特定于C的内容。显而易见的限制是,您尝试打开的库必须使用C ++编译器进行编译,该编译器与您用于编译自己的代码的编译器兼容。在Windows上,这实际上意味着使用相同的MSVC版本。

除此之外,你必须look up the mangled version of the symbol。完成后,可以使用与其匹配的函数/方法指针调用符号。这是won't work on constructors/destructors,按设计。如果您希望创建对象的新实例,则需要库提供的静态工厂方法。

如果库不提供工厂方法,则可以实现通过通用名称链接到目标库的填充程序库,并提供工厂方法。您仍然需要通过函数/方法指针调用各个方法。

  1. 创建一个临时文件夹。
  2. 将填充程序库复制到临时文件夹。
  3. 将重命名为通用名称的目标库复制到临时文件夹中。
  4. 保存LD_LIBRARY_PATH环境变量的值。
  5. 将临时文件夹添加到LD_LIBRARY_PATH
  6. 打开/加载库。
  7. 恢复LD_LIBRARY_PATH的已保存值。
  8. 当然,您必须拥有库公开的任何接口的头文件。一般情况下,只能通过动态库文件重建它 - 主要是因为受损的符号没有所用类型的完整结构信息。例如,即使您可以找到给定类的构造函数,您也不会知道类实例有多大(sizeof)。

答案 2 :(得分:0)

是的,您可以在大多数操作系统上执行您所描述的操作,但是您的操作方式取决于系统,无论系统如何,它都可以为您提供更多的工作。

一般步骤如下:

  1. 加载库
  2. 对于您在库中感兴趣的每个符号,找到它并存储到变量以供以后使用。 (这可以根据需要而不是马上完成。)
  3. 例如,在* nix类型系统上的伪代码(读取:这不会编译!)中,假设您的共享库中包含此代码:

    // I'm declaring this _extern "C"_ to avoid name mangling, making it easier to
    // specify a symbol name for dlsym() later
    extern "C" int myFunction() {
        return 10;
    }
    

    假设这是一个名为libmyFunction.so的库。您的主要应用程序可以是,例如:

    {
        void *handle = dlopen("libmyFunction.so", <flags>);
        if (!handle) return; // error: cannot locate the library!
    
        int (*func)() = (int (*)())dlsym(handle, "myFunction");
        if (!func) return; // error: cannot locate the symbol!
    
        printf("The function returns: %d\n", func());
    }
    

    如果你需要在Windows上执行此操作,概念是相同的,但函数调用是不同的。