Virtual method in a DLL Shared Lib
-
Hi everyone!
I have a little problem that I can't resolve.
I reproduced it in simplier's context to make it easy to understand.So, I try to compile some piece of code to build a SharedLib in Qt.
This SharedLib is a tree of class (2 in this exemple), with an abstract base class and a concrete class.Here's files:
main.cpp
[code]
#include <QCoreApplication>#include <string>
#include <iostream>#include "SharedLib/Concrete.hpp"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);std::cout << "HELLOOOOWWW\n"; Test::SharedLibConcrete foo; foo.test(); return a.exec();
}[/code]
Concrete.hpp
[code]#ifndef SHAREDLIB_CONCRETE_HPP
#define SHAREDLIB_CONCRETE_HPP#include <string>
#include "SharedLib/global.hpp"
#include "SharedLib/Abstract.hpp"namespace Test
{class SHAREDLIB_EXPORT SharedLibConcrete : public SharedLibAbstract
{public: SharedLibConcrete() : SharedLibAbstract() { } ~SharedLibConcrete() = default; std::string init() { return std::string("Yeah, that's ruleZ!"); }
};
} // namespace Test
#endif // SHAREDLIB_CONCRETE_HPP[/code]
Abstract.hpp
[code]#ifndef SHAREDLIB_ABSTRACT_HPP
#define SHAREDLIB_ABSTRACT_HPP#include <string>
#include <iostream>#include "SharedLib/global.hpp"
namespace Test
{class SHAREDLIB_EXPORT SharedLibAbstract
{public: SharedLibAbstract() = default; ~SharedLibAbstract() = default; void test() { std::cout << init(); } std::string init() { return std::string("Not from this :("); }
};
} // namespace Test
#endif // SHAREDLIB_ABSTRACT_HPP[/code]
That's print "HELLOOOOWWW Not from this :("
It's logic but that's not what i want!I tried:
[code] void test() {
std::cout << this->init();
}virtual std::string init() { return std::string("Not from this :("); }[/code]
in Abstract.hpp
but I get:
[code] debug/main.o: In functionZN4Test17SharedLibAbstractC2Ev': D:/Dev/C++/Projects/TestLib/testlib/SharedLib/src/SharedLib/Abstract.hpp:18: undefined reference to
_imp___ZTVN4Test17SharedLibAbstractE'
debug/main.o: In functionZN4Test17SharedLibConcreteC1Ev': D:/Dev/C++/Projects/TestLib/testlib/SharedLib/src/SharedLib/Concrete.hpp:16: undefined reference to
_imp___ZTVN4Test17SharedLibConcreteE'
collect2.exe: error: ld returned 1 exit status[/code]As error at compile time. (Pure virtual also).
my final objective is to declare an interface in the abstract class (an "init" method) which must be implemented in the derived class and called from the base class.
There is no best way than use: [code]static_cast<CRTP*>(this)->plow()[/code]
?
Can't use Virtual (pure, final, ...) method in a shared dll's lib?Regards
Pascal Blétard
-
Are you trying to build Qt Plugin ? You can look at the examples "How to create Qt Plugins". You will be able to achieve your objective very easily.
-
No, not a plugin. It's shared codee between apps of a package.
The problem is not the use of the lib (DLL, SO, whatever. ..) but the use of virtual methods in this lib.
-
I have done it using the simple Qt plugins with pure virtual methods in the base class. It works beautifully. I have not tried your example.
-
Hi,
Are you really sure you are exporting everything correctly ?
-
yeah, i think
Here's a list of all my files of this test project:
testlib.pro
[code]TEMPLATE = subdirsCONFIG +=
SUBDIRS += SharedLib
SomeApp \SomeApp.depends = SharedLib
OTHER_FILES += .qmake.conf[/code]
.qmake.conf
[code]ROOT_DIR = $$PWD/../..
PROJECT_DIR = $$PWD
LIB_DIR = $$ROOT_DIR/CommonBIN_OUT_DIR = $$PWD/../bin/
LIB_OUT_DIR = $$BIN_OUT_DIR/lib/INCLUDEPATH +=[/code]
SomeApp/SomeApp.pro
[code]QMAKE_CXXFLAGS += -std=c++0xTEMPLATE += app
QT += core
QT -= guiCONFIG +=
CONFIG += console
CONFIG += qtwarn_on
CONFIG -= app_bundleINCLUDEPATH +=
$$PWD/src/
$$PROJECT_DIR/SharedLib/src/LIBS += -L$$LIB_OUT_DIR -lSharedLib
DESTDIR = $$BIN_OUT_DIRSOURCES += src/main.cpp[/code]
SomeApp/src/main.cpp
[code]
#include <QCoreApplication>#include <string>
#include <iostream>#include "SharedLib/Concrete.hpp"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);std::cout << "HELLOOOOWWW\n"; Test::SharedLibConcrete foo; foo.test(); return a.exec();
}[/code]
SharedLib/SharedLib.pro
[code]QMAKE_CXXFLAGS *= -std=c++11TEMPLATE = lib
CONFIG += shared
CONFIG +=DEFINES += SHAREDLIB_LIBRARY
INCLUDEPATH += $$PROJECT_DIR/SharedLib/src/
HEADERS +=
src/SharedLib/global.hpp
src/SharedLib/Abstract.hpp
src/SharedLib/Concrete.hppDESTDIR = $$LIB_OUT_DIR[/code]
SharedLib/src/SharedLib/global.hpp
[code]#ifndef DEMOFILE_GLOBAL_HPP
#define DEMOFILE_GLOBAL_HPP#include <QtCore/QtGlobal>
#if defined(SHAREDLIB_LIBRARY)
define SHAREDLIB_EXPORT Q_DECL_EXPORT
#else
define SHAREDLIB_EXPORT Q_DECL_IMPORT
#endif
#endif // DEMOFILE_GLOBAL_HPP[/code]
SharedLib/src/SharedLib/Concrete.hpp
[code]#ifndef SHAREDLIB_CONCRETE_HPP
#define SHAREDLIB_CONCRETE_HPP#include <string>
#include "SharedLib/global.hpp"
#include "SharedLib/Abstract.hpp"namespace Test
{class SHAREDLIB_EXPORT SharedLibConcrete : public SharedLibAbstract
{
public:
SharedLibConcrete() : SharedLibAbstract() {
}
~SharedLibConcrete() = default;std::string plow() { return std::string("Yeah, that's ruleZ!"); }
};
} // namespace Test
#endif // SHAREDLIB_CONCRETE_HPP[/code]
And finally, the SharedLib/src/SharedLib/Abstract
[code]#ifndef SHAREDLIB_ABSTRACT_HPP
#define SHAREDLIB_ABSTRACT_HPP#include <string>
#include <iostream>#include "SharedLib/global.hpp"
namespace Test
{
class SHAREDLIB_EXPORT SharedLibAbstract
{
public:
SharedLibAbstract() = default;
~SharedLibAbstract() = default;void test() { std::cout this->plow(); } std::string plow() { return std::string("Not from this :("); }
};
} // namespace Test
#endif // SHAREDLIB_HPP[/code]
That compiles fine but prints "Not from this :(", logic, it call the same scope method...
But, if i put the "plow" method as virtual, like that (or as pure virtual method):
[code]#ifndef SHAREDLIB_ABSTRACT_HPP
#define SHAREDLIB_ABSTRACT_HPP#include <string>
#include <iostream>#include "SharedLib/global.hpp"
namespace Test
{
class SHAREDLIB_EXPORT SharedLibAbstract
{
public:
SharedLibAbstract() = default;
~SharedLibAbstract() = default;void test() { std::cout this->plow(); } virtual std::string plow() { return std::string("Not from this :("); }
};
} // namespace Test
#endif // SHAREDLIB_HPP[/code]
I get these errors:
[code] ...
debug/main.o: In functionZN4Test17SharedLibAbstractC2Ev': D:/Dev/C++/Projects/TestLib/testlib/SharedLib/src/SharedLib/Abstract.hpp:18: undefined reference to
_imp___ZTVN4Test17SharedLibAbstractE'
debug/main.o: In functionZN4Test17SharedLibConcreteC1Ev': D:/Dev/C++/Projects/TestLib/testlib/SharedLib/src/SharedLib/Concrete.hpp:16: undefined reference to
_imp___ZTVN4Test17SharedLibConcreteE'
collect2.exe: error: ld returned 1 exit status
...[/code]The only way it work is to use CRTP and modify Abstract.hpp and Concrete.hpp to:
SharedLib/src/SharedLib/Concrete.hpp
[code]#ifndef SHAREDLIB_CONCRETE_HPP
#define SHAREDLIB_CONCRETE_HPP#include <string>
#include "SharedLib/global.hpp"
#include "SharedLib/Abstract.hpp"namespace Test
{class SHAREDLIB_EXPORT SharedLibConcrete : public SharedLibAbstract<SharedLibConcrete>
{public: SharedLibConcrete() : SharedLibAbstract() { } ~SharedLibConcrete() = default; std::string plow() { return std::string("Yeah, that's ruleZ!"); }
};
} // namespace Test
#endif // SHAREDLIB_CONCRETE_HPP[/code]
And finally, the SharedLib/src/SharedLib/Abstract
[code]#ifndef SHAREDLIB_ABSTRACT_HPP
#define SHAREDLIB_ABSTRACT_HPP#include <string>
#include <iostream>#include "SharedLib/global.hpp"
namespace Test
{
template <class CRTP>
class SHAREDLIB_EXPORT SharedLibAbstract
{public: SharedLibAbstract() = default; ~SharedLibAbstract() = default; void test() { std::cout << static_cast<CRTP*>(this)->plow(); } std::string plow() { return std::string("Not from this :("); }
};
} // namespace Test
#endif // SHAREDLIB_HPP[/code]
Why i cant use virtual method?
-
Why are your classes now become template classes ?
-
To apply crtp idiom. 'Cause i can't call derived "init" method from base class using virtual method (see error msg when i try to use virtual)
-
Without running a C++ name demangler I have to say that this looks like your compiler doesn't know where to instantiate the virtual table. Make one of your virtual functions non-inline and put it into a .cpp file, that should help.
Also, see here: http://www.parashift.com/c++-faq/link-errs-missing-vtable.html
-
Damn yes !
I'll test it monday. Thank you.