Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QLibrary - import a class

Tags:

c++

qt

qt5

qlibrary

I have a QT library and I want to import it in another project.

Now, since I want that, even when I modify the library, the other project does not need to be compiled again, I started using QLibrary.

But... I can't import a class. Or better, I can import the class, but I can't access its methods.

This is the example I made.

This is the class declaration:

class TESTDLL_LIBSHARED_EXPORT TestDLL_lib
{

public:
    TestDLL_lib();

    int a;
    int b;
    int c;

    int getValues();
}; 

and this the implementation:

#include "testdll_lib.h"

TestDLL_lib::TestDLL_lib()
{
    a = 10;
    b = 20;
    c = 30;
}

int TestDLL_lib::getValues()
{
    return a+b+c;
}

extern "C" TESTDLL_LIBSHARED_EXPORT TestDLL_lib* create_TestDLL_lib()
{
   return new TestDLL_lib();
}

while this is the main file, in the other project:

#include <testdll_lib.h>
#include <QDebug>
#include <QLibrary>

int main(int argc, char *argv[])
{
    QLibrary library("TestDLL_lib");
    if (library.load())
    {
        typedef TestDLL_lib* (*create_TestDLL_lib_fun)();
        create_TestDLL_lib_fun create_TestDLL_lib = (create_TestDLL_lib_fun)library.resolve("create_TestDLL_lib");

        if (create_TestDLL_lib)
        {
            TestDLL_lib *myClassInstance = create_TestDLL_lib();

            if (myClassInstance)
            {
                //qDebug() << QString::number(myClassInstance->getValues());
                qDebug() << QString::number(myClassInstance->a) + " " + QString::number(myClassInstance->b) + " " + QString::number(myClassInstance->c);
            }
        }

        library.unload();
    }
}

Now, I can access all the data values (a, b, c) of the object myClassInstance (and, if i change them in the DLL, they also get changed in the program without a rebuild) but I can't call myClassInstance->getValues() because I get

main.obj:-1: error: LNK2001: unresolved external symbol "__declspec(dllimport) public: int __thiscall TestDLL_lib::getValues(void)" (__imp_?getValues@TestDLL_lib@@QAEHXZ)

How can I solve this? Is it possible to call methods from imported classes?

Thank you..

like image 833
frarugi87 Avatar asked Oct 07 '14 10:10

frarugi87


1 Answers

You cannot call methods on classes imported at runtime. This is because the compiler links these calls at compile-time and not at run-time (which it cannot do). A way out is provided by our good ol' friend, the vtable:

You can, call virtual methods on classes implementing an interface (the interface is not "imported" at runtime). That means to define a class defining the interface using virtual (possibly pure virtual) methods. TestDLL_lib would then inherit that interface, implementing the methods. You would refer to the TestDLL_lib instance via that interface and call methods trough that interface, effectively calling them trough the vtable of the interface, which is "superseded" by TestDLL_libs vtable.

Don't forget to make your d'tor virtual and to add a virtual dtor to the interface. If you don't do that you cannot safely delete instance trough the interface pointer.

I might also explain why you can access members, but not call functions on "imported" classes. The members are accessed by memory location, and the memory location is solely defined by the compiler. Thus the compiler generates the code to access members without ever referring to any of the classes' symbols (methods and so on). This in turns leads to no linkage dependency. Note however that you would need to recompile both the DLL and the application using the DLL if you change the class, e.g. adding or removing a member, since that changes the memory layout.

class TestInterface
{
public:
    virtual ~TestInterface()
    {
    }

    virtual int getValues() = 0;
}

class TESTDLL_LIBSHARED_EXPORT TestDLL_lib : public TestInterface
{

public:
    TestDLL_lib();
    virtual ~TestDLL_lib();

    int a;
    int b;
    int c;

    int getValues() override; // MSVC may not support "override"
}; 

// return pointer to interface!
// TestDLL_lib can and should be completely hidden from the application
extern "C" TESTDLL_LIBSHARED_EXPORT TestInterface *create_TestDLL_lib()
{
    return new TestDLL_lib();
}
like image 122
dom0 Avatar answered Oct 22 '22 15:10

dom0