C++ problem: undefined reference when creating entry point in shared library clang

Problem Description:

I have a problem that I don't understand. My project is very simple now. I have a shared library engine called by my executable. I try to move entry points in my shared library, so the executable part is only functions and some classes to be created.

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

  1. entry.h

  2. BaseGame.h

  3. Application.cpp

This 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;
};

In my executable file, I have two files entry CPP defines create_game and my_game.h. They are inherited from games.

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 encounter linker errors 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

This is how I build shared libraries:

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%

This 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 the game is exported. Does anyone see anything wrong? PS: I use Clang as the compiler.

Solution 1:

Your example is not complete. I assume that the destructor ~game() is actually defined in a cpp file in the shared library. And my_game.h needs to include BaseGame.h (because myGame is derived from game). So basically my_game.cpp in the executable file, you will finally see the definition of game as shown in the figure, including__ declspec(dllexport). However, executable files should not be seen__ Declspec (dllexport) (that is, exe should not export game), but should see__ Declspec (dllimport) (that is, exe should import game). Therefore, you want to define gamewith when building DLLs and executables__ declspec(dllexport)__declspec(dllimport)

The typical way to conditionally own one or another is to use macros and change their definitions according to preprocessor symbols, which is different for exe and dll. For example (adapted from official documents):

// Either in BaseGame.h or in a dedicated header
// that gets included by BaseGame.h.
#ifdef EXPORT_GAME_SYMBOLS
   #define GAME_DECLSPEC  __declspec(dllexport)
#else
   #define GAME_DECLSPEC  __declspec(dllimport)
#endif

// BaseGame.h
class GAME_DECLSPEC game
{ ... class definition ... };

When building DLLs, you do EXPORT_GAME_SYMBOLS is defined in the preprocessor (-DEXPORT_GAME_SYMBOLS). However, when building the executable, you did not define EXPORT_GAME_SYMBOLS.

The linker only complains about destructors, game, because in your example, only destructors have non inline implementations. The default constructor is inline (game() = Default;, It means that the linker does not need to export to find the implementation), and other functions are pure functions (it means that they have no initial implementation). Therefore, the alternative here is to completely delete the export / import content game and define the inline destructor (virtual ~game() = default;). But of course, if everything is inline, you don't need to build a shared library to start

sidenote:

__ declspec(dllexport)on is meaningless because it is in cppdll file, which means that the code in the executable file will never see Application (unless you \include "Application.cpp", you should not do this.) Therefore, there is no need to export symbols.

As others have pointed out, it is not common for main() to be included in header files. You can move it to your exe project, such as my_game.cpp or special main.cpp.

Solution 2 (solution to editor's problem):

This class of game is missing a destructor definition. I suggest this default:

The above is only an introduction to some solutions. Please check the whole content. Please add the official account below and reply 001 to check it.

Official account has many programming books and other practical tools with the highest scores, which are absolutely easy to use and can be used with confidence

If you feel helped, you can also follow the official account - publish useful information and resources regularly

Tags: C++ programming language

Posted by scbookz on Wed, 03 Aug 2022 22:33:26 +0530