#Undefined Reference Error - Classes

84 messages · Page 1 of 1 (latest)

analog ether
#

Hello !
I'm making a SDL projectand I've got an almost empty "Physic Engine" Class with a static attribute (static SDL_Renderer *) and a basic "Game" class that instantiates a physic engine attribute.

I've included physic engine.cpp in game.cpp but it still says it's undefined...

/usr/bin/ld: /tmp/ccV9PwDN.o: warning: relocation against `_ZN12PhysicEngine8rendererE' in read-only section `.text' /usr/bin/ld: /tmp/ccV9PwDN.o: in function `PhysicEngine::PhysicEngine(SDL_Window*)': main.cpp:(.text+0x1f4): undefined reference to `PhysicEngine::renderer' /usr/bin/ld: /tmp/ccV9PwDN.o: in function `Game::Init(char const*)': main.cpp:(.text+0x39a): undefined reference to `PhysicEngine::renderer' /usr/bin/ld: warning: creating DT_TEXTREL in a PIE collect2: error: ld returned 1 exit status
If you want me to provide code don't hesitate to tell me (you can even ping me)

thorn iglooBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

analog ether
#

Relocation error - Classes

#

Underined Reference Error - Classes

#

Undefined Reference Error - Classes

latent summit
feral atlas
#

I've included physic engine.cpp in game.cpp
You're not supposed to do that.

#

It's going to be difficult to figure out without seeing the code. Maybe you made a typo somewhere, maybe you forgot the definition, maybe you thought the declaration would count as a definition.

#

Typically you just CTRL+click on PhysicEngine::renderer and try to find the definition.

slim quest
#

send us your file structure

slim quest
# analog ether Hello ! I'm making a SDL projectand I've got an almost empty "Physic Engine" Cla...

you are not supposed to include cpp fiels either

this is how you share code between translation units (.cpp files) in C++:

You create so called header files (usually with a .h extension). These header files contain all the code that is absolutely required by other translation units to use the code of another translation unit.

This includes:

  • class definitions
  • function DECLARATIONS
  • template definitions
  • definitions of inline marked things

So this is how you would create a simple class with a member function with the header + source combination:

//foo.h

class Foo{
  public:
    static int x; //declare the member in foo.h because
                  //the other TUs don't need the definition
                  //at compile time.
    void f() const;
};

//foo.cpp

int Foo::x = 5; //define Foo::x in foo.cpp

void Foo::f() const {} // define Foo::f in foo.cpp

then what will happen is you include foo.h in you other source files and use everything as usual.

When you build your project all translation units are compiled separately and they will produce object files. These files contain the compiled code of the ".cpp" files.

Then a program called a linker links all these files together into one executable.

When compilation happens, the compiler doesn't care if the definition of a symbol (function name, variable name etc.) is available, it only cares if there is a declaration. When linking happens, the linker links the declarations to definitions.

What you see there is a linker error, which basically means that the linker didn't find a definition to link to your static member variable.

#

and what #include does is it just copy pastes whatever file you included into the place of the include directive

analog ether
#

hmm okay
That's basically what I understood about headers but I have surely done something wrong
If including .cpp file is incorrect, then my file structure is awful...

#

here's physic_engine.h :

#pragma once
#include <SDL3/SDL.h>

//all my homies hate collision detections

class PhysicEngine {
    public:
        static const int gravity = 100;
        static const int FPS = 60;
        static const int DeltaTime = 1000/FPS;

        static SDL_Renderer * renderer;
    
        PhysicEngine(SDL_Window * window);

        static void MoveDynamicAndCollideWithSolid();
};```
and physic engine.cpp :
```c++
#include "physic_engine.h"

static SDL_Renderer * renderer = nullptr;

PhysicEngine::PhysicEngine(SDL_Window * window) {
    renderer  = SDL_CreateRenderer(window, NULL);
};```
#

and here's game.h :

#pragma once
#include "physic_engine.h"
#include <SDL3/SDL.h>
#include <vector>

class Game {
    public:
        PhysicEngine * ph;

        const std::vector<int> WINDOW_SIZE = {1080, 960};

        Game();
        ~Game();

        void Init(const char* title);

        [[unrelated stuff]]
};```
and game.cpp ;
```c++
#include "game.h"
#include "texture_manager.cpp"
#include "GameObject.cpp"
#include "physic_engine.cpp"
#include <iostream>

GameObject * player;
GameObject * player2;

PhysicEngine * ph;

Game::Game() {}
Game::~Game() {}

void Game::Init(const char* title) {
    if (SDL_Init(SDL_INIT_VIDEO)) {
        std::cout << "Init worked" << std::endl;

        window = SDL_CreateWindow(title, WINDOW_SIZE[0], WINDOW_SIZE[1], SDL_WINDOW_RESIZABLE);

        ph = new PhysicEngine(window);

        SDL_SetRenderDrawColor(PhysicEngine::renderer, 0, 0, 0, 0);

        is_running = true;

        SDL_SetWindowPosition(window, 0, 100);

        player = new GameObject("../Assets/Image/Pawn/pawn.bmp", renderer, 0, 0);
        player2 = new GameObject("../Assets/Image/Pawn/pawn.bmp", renderer, 500, 0);
    }

    else is_running = false;
}
[[also unrelated stuff]]
#

(idk if that's what you meant by file structure but here's all of the code, there should be all the needed info)

analog ether
slim quest
slim quest
#

it says "in the scope of"

#

you define members of the PhysicsEngine class

#

so you need to tell the compiler

#

what you did right now with static SDL_Renderer * renderer = nullptr; is define a global variable of type SDL_Renderer* that is only available in that cpp file (because that is what static means when applied to global namespace stuff)

analog ether
#

ooh

#

okay

#

it gives me this though

slim quest
#

you dont need to put static

#

only inside the class definition

#

not when defining the member outside the class

slim quest
#

and then no need for the definition in the c++ file if you dont want to

analog ether
#

what's inline ?

#

is it to give it a value in the header ?

slim quest
#

its meaning is "allow multiple definitions of this symbol if all the definitions match"

#

because header files are copy pasted if you put a defintion of lets say a function into a header, without inline, and you include the header in multiple TUs (cpp files), at linking the linker will see multiple definitions of the same function

#

and it will give you an error

#

inline fixes this, but it really is just a workaround tool, but sometimes its nice

#

for example when creating a library, its nice to be able to put static constant members into the header file together with the definition because that way the user of the library can see what the constant is without looking at the full source code

analog ether
#

I see

#

it works now !
But when I try to replace the #include #include "physic_engine.cpp" by #include "physic_engine.h" it says :
/usr/bin/ld: /tmp/ccep2f9L.o: in function `Game::Init(char const*)': main.cpp:(.text+0x35b): undefined reference to `PhysicEngine::PhysicEngine(SDL_Window*)'
Even though PhysicEngine::PhysicEngine(SDL_Window*) is well defined in the .cpp ?

thorn iglooBOT
#

@analog ether Has your question been resolved? If so, type !solved :)

slim quest
#

did you use inline?

analog ether
#

well I run a bash script
with export LD_LIBRARY_PATH=./SDL/build
then g++ main.cpp -o main -L./SDL/build -lSDL3 -ISDL/include

analog ether
#

and I deleted the declaration in .cpp

slim quest
#

not just main.cpp

analog ether
#

really ?

#

oh

slim quest
#

g++ main.cpp physics_engine.cpp game.cpp -o main -L./SDL/build -lSDL3 -ISDL/include

slim quest
analog ether
#

okay, that makes sense actually

#

oh god

#

the error

#

is it because I still include .cpps ?

slim quest
#

multiple definition of ...

#

it basically means what we discussed when defining stuff in headers (or practically anything that you include in other files)

analog ether
#

yeah, it also makes sense

#

I was just impressed by the length of the error message

slim quest
analog ether
#

is there an order for compilation ?
Like is g++ main.cpp game.cpp different from g++ game.cpp main.cpp ?

analog ether
#

yeah ok

slim quest
#

they are called translation units for a reason

analog ether
#

that's what I thought but who knows

slim quest
#

they are translated as one unit not dependent on each other

#

some compilers compile multiple TUs parallel on multiple threads aswell ^^

analog ether
#

nice

#

it wooooooooorks

#

there were ~15 errors

#

I included sdl3/sdl.h in another header

#

and it all works

#

thanks

#

I'll go looking for remnants of illogic code now

#

your explanations are great, thank you so very much

slim quest
analog ether
#

sadly it's not always the case y'know
I moved the renderer to the game class and I got it work all clean
thank again

#

!solved