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&#40;&#41;;
    

    }[/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 function ZN4Test17SharedLibAbstractC2Ev': D:/Dev/C++/Projects/TestLib/testlib/SharedLib/src/SharedLib/Abstract.hpp:18: undefined reference to_imp___ZTVN4Test17SharedLibAbstractE'
    debug/main.o: In function ZN4Test17SharedLibConcreteC1Ev': 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.


  • Lifetime Qt Champion

    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 = subdirs

    CONFIG +=

    SUBDIRS += SharedLib
    SomeApp \

    SomeApp.depends = SharedLib

    OTHER_FILES += .qmake.conf[/code]

    .qmake.conf
    [code]ROOT_DIR = $$PWD/../..
    PROJECT_DIR = $$PWD
    LIB_DIR = $$ROOT_DIR/Common

    BIN_OUT_DIR = $$PWD/../bin/
    LIB_OUT_DIR = $$BIN_OUT_DIR/lib/

    INCLUDEPATH +=[/code]

    SomeApp/SomeApp.pro
    [code]QMAKE_CXXFLAGS += -std=c++0x

    TEMPLATE += app

    QT += core
    QT -= gui

    CONFIG +=
    CONFIG += console
    CONFIG += qtwarn_on
    CONFIG -= app_bundle

    INCLUDEPATH +=
    $$PWD/src/
    $$PROJECT_DIR/SharedLib/src/

    LIBS += -L$$LIB_OUT_DIR -lSharedLib
    DESTDIR = $$BIN_OUT_DIR

    SOURCES += 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&#40;&#41;;
    

    }[/code]

    SharedLib/SharedLib.pro
    [code]QMAKE_CXXFLAGS *= -std=c++11

    TEMPLATE = lib

    CONFIG += shared
    CONFIG +=

    DEFINES += SHAREDLIB_LIBRARY

    INCLUDEPATH += $$PROJECT_DIR/SharedLib/src/

    HEADERS +=
    src/SharedLib/global.hpp
    src/SharedLib/Abstract.hpp
    src/SharedLib/Concrete.hpp

    DESTDIR = $$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 function ZN4Test17SharedLibAbstractC2Ev': D:/Dev/C++/Projects/TestLib/testlib/SharedLib/src/SharedLib/Abstract.hpp:18: undefined reference to_imp___ZTVN4Test17SharedLibAbstractE'
    debug/main.o: In function ZN4Test17SharedLibConcreteC1Ev': 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?


  • Lifetime Qt Champion

    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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.