Search code examples
c++clangdllexportentry-point

Undefined reference when creating entry point in shared library clang


I have an issue I don't understand. My project is quite simple for now. I have a shared library Engine which is called by my executable. I'm trying to move the entry point inside my shared library, so the executable part only has functions and some class to create.

---EDIT: I edit to post the project as it is reproductible easily.

To do so, I have these files in my shared library:

  1. entry.h
  2. BaseGame.h
  3. Application.cpp

Here is entry.h

#include "BaseGame.h"

extern game* create_game();

int main(int argc, char *argv[])
{
    auto testgame = create_game();
    delete testgame;
    return 0;
}

BaseGame.h

class __declspec(dllexport) game
{
public:
    game() = default;
    virtual ~game();
    virtual bool initialize() =0;
    virtual bool update(float deltaTime) =0;
    virtual bool render(float deltaTime) =0;
    virtual void on_resize() =0;
};

Application.cpp

#include "BaseGame.h"
class __declspec(dllexport) Application
{
public:
    Application()=default;
    ~Application()=default;
};

And in my executable, I have two files entry.cpp which defines create_game and my_game.h which inherited from game.

entry.cpp

#include <entry.h>
#include "my_game.h"

game* create_game()
{
    return new myGame;
}

and my_game.h:

class myGame : public game
{
public:
    myGame(){};
    ~myGame() override = default;
    bool initialize() override;
    bool update(float deltaTime) override;
    bool render(float deltaTime) override;
    void on_resize() override;
};

my_game.cpp:

#include "my_game.h"

bool myGame::initialize()
{
    return true;
}
bool myGame::update(float deltaTime)
{
    return true;
}
bool myGame::render(float deltaTime)
{
    return true;
}
void myGame::on_resize()
{
}

What I don't understand is that I always get an linker error when building my exe:

Création de la bibliothèque ..\bin\testbed.lib et de l'objet ..\bin\testbed.exp
entry-3b33f2.o : error LNK2019: symbole externe non résolu "public: virtual __cdecl game::~game(void)" (??1game@@UEAA@XZ) référencé dans la fonction "public: virtual __cdecl myGame::~myGame(void)" (??1myGame@@UEAA@XZ)
..\bin\testbed.exe : fatal error LNK1120: 1 externes non résolus

Also here is how i build my shared library:

SET assembly=Engine
SET compilerFlags=-g -shared -Wvarargs -Wall -Werror
SET includeFlags=-Isrc
SET linkerFlags=-luser32
SET defines=-D_DEBUG_EG -DGEXPORT -D_CRT_SECURE_NO_WARNINGS

ECHO "Building %assembly%%..."
clang++ %cFilenames% %compilerFlags% -o ../bin/%assembly%.dll %defines% %includeFlags% %linkerFlags%

And here is my executable:

SET assembly=testbed
SET compilerFlags=-g 
REM -Wall -Werror
SET includeFlags=-Isrc -I../Engine/src/
SET linkerFlags=-L../bin/ -lEngine.lib
SET defines=-D_DEBUG_EG -DGIMPORT
clang++ %cFilenames% %compilerFlags% -o ../bin/%assembly%.exe %defines% %includeFlags% %linkerFlags%

Even if game is exported. Does anyone see something wrong? PS: I'm using Clang as the compiler.


Solution

  • The game class lacks a destructor definition. I suggest making it default:

    class myGame : public game
    {
    public:
        myGame(){};
        ~myGame() override = default;          // here
        bool initialize() override;
        bool update(float deltaTime) override;
        bool render(float deltaTime) override;
        void on_resize() override;
    };
    

    You may also remove the default constructor and destructor from myGame. It'll be default constructible and have a virtual destructor by default.

    class myGame : public game
    {
    public:
        // myGame(){};                   // remove
        // ~myGame() override = default; // remove
        // ...
    

    Other notes:

    • All your header files should have header guards to prevent including the same file twice in one translation unit.
    • my_game.h should #include "BaseGame.h" to get the definition of game.