C++ load shared library and extract class implemen

2019-04-02 13:34发布

In C++, is it possible to load a shared library at execution time?

I want the user to choose which shared library to be loaded at runtime, without recompiling the whole program.

dlopen() is a solution for C, but my program is written is C++/Qt, and the symbol to extract are Qt-style class, is there a more "c++" way to do that.

3条回答
Juvenile、少年°
2楼-- · 2019-04-02 14:16

Yes it's possible to do what you're describing on most operating systems, but how you do it is dependent on the system and regardless of the system it's definitely a bit more work on your end to make it happen.

The general steps are:

  1. load the library
  2. for each symbol you're interested in within the library, locate it and store to a variable for later use. (This can be done as-needed, rather than right away.)

For example, in pseudo-code (read: this won't compile!) on a *nix type system, lets assume your shared library has this in it:

// 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;
}

Assume this is in a library called libmyFunction.so. Your main application could, for example:

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

If you need to do this on Windows, the concept is the same but the function calls are different.

查看更多
何必那么认真
3楼-- · 2019-04-02 14:31

You can do it in Qt using QLibrary in two ways. The following example calls a function from a shared library at runtime in two different ways:

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

The code for the shared library is as follows:

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;
}
查看更多
We Are One
4楼-- · 2019-04-02 14:39

If the target library itself, or at least its specification, is under your control, then you shouldn't be using QLibrary - use the Qt plugin system instead. It doesn't require the call-via-pointer gymnastics otherwise needed.

If you insist on using a dlopen-like mechanism, there is nothing C-specific about QLibrary. The obvious limitation is that the library that you're trying to open must have been compiled with a C++ compiler that's ABI-compatible to the one you use to compile your own code. On Windows that really means using the same MSVC version.

Apart from that, you'll have to look up the mangled version of the symbol. Once you've done that, you can call the symbol using a function/method pointer that matches it. This won't work on constructors/destructors, by design. If you wish to create new instances of objects, you'll need a static factory method provided by the library.

If the library doesn't provide factory methods, you can implement a shim library that links to the target library by a generic name and does provide factory methods. You'll still need to call individual methods by function/method pointers.

  1. Create a temporary folder.
  2. Copy the shim library to the temporary folder.
  3. Copy the target library renamed to the generic name, into the temporary folder.
  4. Save the value of LD_LIBRARY_PATH environment variable.
  5. Prepend the temporary folder to LD_LIBRARY_PATH.
  6. Open/load the library.
  7. Restore the saved value of LD_LIBRARY_PATH.

Of course, you must have the header file for whatever interface the library exposes. It can't be, generally, reconstructed given just a dynamic library file - primarily because the mangled symbols don't have full structural information for the used types. For example, even if you can find a constructor for a given class, you won't know how big is the class instance (its sizeof).

查看更多
登录 后发表回答