PDA

View Full Version : How to export C symbols from qt dynamic library



rwhartzell
8th August 2016, 03:30
i've been trying every incantation of extern "C" that I could find on the internet but I'm unable to get un-mangeled symbols from a basic Qt shared library on OS X. After two days of trying, I have no idea how to do it.
Would someone please be kind enough to show me the correct way?

This is all I've been able to get.

$nm -gU ./libTestLib.1.0.0.dylib
0000000000003550 T __ZN7TestLib10printStuffEv
0000000000003530 T __ZN7TestLibC1Ev
0000000000003520 T __ZN7TestLibC2Ev

testlib_global.h


#ifndef TESTLIB_GLOBAL_H
#define TESTLIB_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(TESTLIB_LIBRARY)
# define TESTLIBSHARED_EXPORT Q_DECL_EXPORT
#else
# define TESTLIBSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // TESTLIB_GLOBAL_H


testlib.h

#ifndef TESTLIB_H
#define TESTLIB_H

#include "testlib_global.h"

class TESTLIBSHARED_EXPORT TestLib {

public:
TestLib();
void printStuff();
};

#endif // TESTLIB_H


testlib.cpp

#include "testlib.h"
#include <QDebug>


TestLib::TestLib() {}

void TestLib::printStuff() {
qDebug() << "Printing Stuff";
}


TestLib.pro

#-------------------------------------------------
#
# Project created by QtCreator 2016-08-05T21:32:46
#
#-------------------------------------------------

QT += widgets

TARGET = TestLib
TEMPLATE = lib

DEFINES += TESTLIB_LIBRARY

SOURCES += testlib.cpp

HEADERS += testlib.h\
testlib_global.h

unix {
target.path = /usr/lib
INSTALLS += target
}

anda_skoa
8th August 2016, 09:50
There are no C symbols in your code snippets, only C++ code.
Did you forget to post something?

Cheers,
_

rwhartzell
8th August 2016, 19:51
There are no C symbols in your code snippets, only C++ code.
Did you forget to post something?

Cheers,
_

No there isn't I just didn't explain the problem correctly. The problem is that QLibrary::resolve() requires that the symbols are export as a c function. So I can load the library no problem but cannot resolve any symbols.

Per the docs... http://doc.qt.io/qt-5/qlibrary.html

The symbol must be exported as a C function from the library for resolve() to work. This means that the function must be wrapped in an extern "C" block if the library is compiled with a C++ compiler.

Thats why I've tried every way I could find using extern "C" but none have worked because the exported symbols are always the same as above. Maybe there is some other way to resolve the symbols but I've not seen any other type of example.

Robert

anda_skoa
8th August 2016, 20:18
You need the extern "C" block to tell the C++ compiler that a function is a C function.
But you still need such a C function.

Your code does not have any stand-alone (non-member) function at all.

Cheers,
_

rwhartzell
8th August 2016, 20:35
So how do you load and use a c++ library at run time? Does it have to be compiled in at compile time?

jefftee
9th August 2016, 04:53
I might be mistaken, but it seems you're wanting to export the symbols at run-time for a Qt dynamic library itself. Symbols are exported by the linker, so I don't think you can dynamically export different symbols at run-time, etc.

Radek
9th August 2016, 08:25
You cannot link class methods "really dynamically" (using QLibrary::load() and QLibrary::resolve()) in Qt. You can only link C-functions. You find some info here

http://stackoverflow.com/questions/26234327/qlibrary-import-a-class

Also, the stackoverflow answer contains a bit of helpful cheese using virtual functions,

anda_skoa
9th August 2016, 08:42
So how do you load and use a c++ library at run time?

You provide one or more C functions that the host program can resolve and use.

Or when both host and plugin are using Qt, just by using Qt's plugin infrastructure
http://doc.qt.io/qt-5/qtwidgets-tools-plugandpaint-app-example.html

Cheers,
_

rwhartzell
9th August 2016, 20:36
I might be mistaken, but it seems you're wanting to export the symbols at run-time for a Qt dynamic library itself. Symbols are exported by the linker, so I don't think you can dynamically export different symbols at run-time, etc.

Mostly i'm just confused about how it all works lol


You cannot link class methods "really dynamically" (using QLibrary::load() and QLibrary::resolve()) in Qt. You can only link C-functions. You find some info here

http://stackoverflow.com/questions/26234327/qlibrary-import-a-class

Also, the stackoverflow answer contains a bit of helpful cheese using virtual functions,

Tha was very helpful, thanks.


You provide one or more C functions that the host program can resolve and use.

Or when both host and plugin are using Qt, just by using Qt's plugin infrastructure
http://doc.qt.io/qt-5/qtwidgets-tools-plugandpaint-app-example.html

Cheers,
_

The problem i'm actually trying to solve is that I have a plugin manager class for my app that i wish to move out of my app and into a stand alone library. Your comments got me thinking I was looking entirely in the wrong direction.


I might be mistaken, but it seems you're wanting to export the symbols at run-time for a Qt dynamic library itself. Symbols are exported by the linker, so I don't think you can dynamically export different symbols at run-time, etc.


Added after 7 minutes:

So after all your very helpful comments and some more research I came up with another test library that I think is closer to what I was looking for.

How does this look?

testlib.h

#ifndef TESTLIB_H
#define TESTLIB_H

class TestLib {

public:
TestLib();
virtual ~TestLib();

virtual int getValue();
virtual void setValue(int y);

private:
int x;
};

#endif // TESTLIB_H

testlib.cpp

#include "testlib.h"
#include <QtCore/QDebug>

extern "C" TestLib* create_obj() {
return new TestLib;
}

extern "C" void destroy_obj(TestLib* obj) {
delete obj;
}

TestLib::TestLib() {
x = 0;
}

TestLib::~TestLib() {}

int TestLib::getValue() {
return x;
}

void TestLib::setValue(int y) {
x = y;
}

main.cpp

#include "../TestLib/testlib.h"
#include <QtCore/QLibrary>
#include <QtCore/QDebug>

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

QLibrary extLib("TestLib");
if (!extLib.load()) {
qDebug() << extLib.errorString();
exit(1);
}

TestLib* (*create)();
void (*destroy)(TestLib*);

create = (TestLib* (*)())extLib.resolve("create_obj");
destroy = (void (*)(TestLib*))extLib.resolve("destroy_obj");

TestLib* testLib = (TestLib*) create();

int x, y = 5;
testLib->setValue(y);
qDebug() << "x =" << (x = testLib->getValue());

destroy(testLib);

return 0;
}

d_stranz
10th August 2016, 04:35
Typically a plugin architecture defines one or more virtual classes which provide the "interface" methods that all compatible plugins must implement. All of the methods in the interface class are pure virtual, that is, they have no code behind them and it is impossible to construct an instance. The plugin internally implements a concrete class that inherits from the interface class and provides implementations (i.e. real code) for the virtual functions defined by the interface. So in your example, the interface class is ITestLib, and has two virtual methods, int getValue() and void setValue( int ).

The "create" method is present in all plugins (if you choose to do it that way) and returns an ITestLib pointer to a concrete instance. So in your case, class TestLib inherits publicly from class ITestLib and provides concrete implementations of getValue() and setValue(). create_obj() as implemented in TestLib returns a TestLib* instance as an ITestLib * pointer.

So your main() would be implemented as:



#include "../TestLib/ITestLib.h"
#include <QtCore/QLibrary>
#include <QtCore/QDebug>

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

QLibrary extLib("TestLib");
if (!extLib.load()) {
qDebug() << extLib.errorString();
exit(1);
}

ITestLib* (*create)();
void (*destroy)(ITestLib*);

create = (ITestLib* (*)())extLib.resolve("create_obj");
destroy = (void (*)(ITestLib*))extLib.resolve("destroy_obj");

ITestLib* testLib = (ITestLib*) create();

int x, y = 5;
testLib->setValue(y);
qDebug() << "x =" << (x = testLib->getValue());

destroy(testLib);

return 0;
}

and ITestLib.h would look like:



#ifndef ITESTLIB_H
#define ITESTLIB_H

class ITestLib
{
public:
virtual int getValue() = 0;
virtual void setValue(int y) = 0;

protected:
ITestLib() {}
virtual ~ITestLib() {}
};

#endif // ITESTLIB_H


TestLib.h would be the same, except that now class TestLib inherits from ITestLib:



#include "ITestLib.h"

class TestLib : public ITestLib
{
public:
TestLib();
// ...
};