Home > Enterprise >  Multiple Definition Error and Undefined Reference Error in CPP and SDL2
Multiple Definition Error and Undefined Reference Error in CPP and SDL2

Time:02-03

I have been trying to make a simple game in SDL. I Found that keeping every thing in on file was getting messy so i moved some functions into their own file and set up a global header file to store all the global things i would use. but from there it has been all errors!

Files:

-Main.cpp
-Makefile
-Assets/
  -Player.bmp
-Build/
  -build.deb
-Modules/
  -Global.h
  -HandleKeys.cpp
  -HandleKeys.h

Main.cpp:

#include <stdio.h>
#include "Modules/Global.h"
#include "Modules/HandleKeys.h"

//Starts up SDL and creates window
bool init();

//Loads media
bool loadMedia();

extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;

//Frees media and shuts down SDL
void close();

//Loads individual image
SDL_Surface* loadSurface( std::string path );

//The window we'll be rendering to
SDL_Window* gWindow = NULL;
    
//The surface contained by the window
SDL_Surface* gScreenSurface = NULL;

//The images that correspond to a keypress
SDL_Surface* Entity[ KEY_PRESS_SURFACE_TOTAL ];

//Current displayed image
SDL_Surface* gCurrentSurface = NULL;

int PlayerX;
int PlayerY;

bool init()
{
    //Initialization flag
    bool success = true;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
        success = false;
    }
    else
    {
        //Create window
        gWindow = SDL_CreateWindow( title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == NULL )
        {
            printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
            success = false;
        }
        else
        {
            //Get window surface
            gScreenSurface = SDL_GetWindowSurface( gWindow );
        }
    }

    return success;
}

string get(list<string> _list, int _i){
    list<string>::iterator it = _list.begin();
    for(int i=0; i<_i; i  ){
          it;
    }
    return *it;
}

bool loadMedia()
{
    //Loading success flag
    bool success = true;
    for( int i = 0; i <= KEY_PRESS_SURFACE_TOTAL;   i )
    {
        if (i != KEY_PRESS_SURFACE_TOTAL){
            Entity[ PLAYER_SURFACE ] = loadSurface( get(EntityPaths, i) );
        }
    }
    return success;
} 
void close()
{
    //Deallocate surfaces
    for( int i = 0; i < KEY_PRESS_SURFACE_TOTAL;   i )
    {
        SDL_FreeSurface( Entity[ i ] );
        Entity[ i ] = NULL;
    }

    //Destroy window
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;

    //Quit SDL subsystems
    SDL_Quit();
}

SDL_Surface* loadSurface( std::string path )
{
    //Load image at specified path
    SDL_Surface* loadedSurface = SDL_LoadBMP( path.c_str() );
    if( loadedSurface == NULL )
    {
        printf( "Unable to load image %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
    }

    return loadedSurface;
}



int main( int argc, char* args[] )
{
    //Start up SDL and create window
    if( !init() )
    {
        printf( "Failed to initialize!\n" );
    }
    else
    {
        //Load media
        if( !loadMedia() )
        {
            printf( "Failed to load media!\n" );
        }
        else
        {   
            //Main loop flag
            bool quit = false;

            //Event handler
            SDL_Event e;

            //Set default current surface
            gCurrentSurface = Entity[ PLAYER_SURFACE ];

            //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                    HandelKey(e);
                }
                SDL_Rect rect;
                rect.x = PlayerX;
                rect.y = PlayerY;
                rect.w = 0;
                rect.h = 0;
                //Apply the current image
                SDL_BlitSurface( gCurrentSurface, NULL, gScreenSurface, &rect );
            
                //Update the surface
                SDL_UpdateWindowSurface( gWindow );
            }
        }
    }

    //Free resources and close SDL
    close();

    return 0;
}

Makefile:

#OBJS specifies which files to compile as part of the project
OBJS = Main.cpp

#MODS specifies which Modules to Compile. 
#NOTE: only add .cpp files, no .h files!
MODS = Modules/HandleKeys.cpp

#CC specifies which compiler we're using
CC = g  

#COMPILER_FLAGS specifies the additional compilation options we're using
# -w suppresses all warnings
COMPILER_FLAGS = -w

#LINKER_FLAGS specifies the libraries we're linking against
LINKER_FLAGS = -lSDL2

#OBJ_NAME specifies the name of our exectuable
OBJ_NAME = Build/build.deb

#This is the target that compiles our executable
all : $(OBJS)
    $(CC) $(OBJS) $(MODS) $(COMPILER_FLAGS) $(LINKER_FLAGS) -o $(OBJ_NAME)

Global.h

#ifndef __GLOBAL_H__
#define __GLOBAL_H__
//Included by all files!
#include <SDL2/SDL.h>
#include <string>
#include <list>
using namespace std;

extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;

extern char* title = "Top Down";
enum Entity
{
    PLAYER_SURFACE,
    KEY_PRESS_SURFACE_TOTAL
};

extern list<string> EntityPaths
{
    "Assets/Player.bmp",
};

//Starts up SDL and creates window
extern bool init();

//Loads media
extern bool loadMedia();

//Frees media and shuts down SDL
extern void close();

//Loads individual image
extern SDL_Surface* loadSurface( std::string path );

//The window we'll be rendering to
extern SDL_Window* gWindow;
    
//The surface contained by the window
extern SDL_Surface* gScreenSurface;

//The images that correspond to a keypress
extern SDL_Surface* Entity[ KEY_PRESS_SURFACE_TOTAL ];

//Current displayed image
extern SDL_Surface* gCurrentSurface;

extern int PlayerX;
extern int PlayerY;

extern const int PlayerSpeed;
#endif

HandleKeys.cpp:

#include "Global.h"
#include "HandleKeys.h"

void HandleKey(SDL_Event e){
    if( e.type == SDL_KEYDOWN )
    {
        //Select surfaces based on key press
        switch( e.key.keysym.sym )
        {
            case SDLK_UP:
            PlayerY -= PlayerSpeed;
            break;

            case SDLK_DOWN:
            PlayerY  = PlayerSpeed;
            break;

            case SDLK_LEFT:
            PlayerX -= PlayerSpeed;
            break;

            case SDLK_RIGHT:
            PlayerX  = PlayerSpeed;
            break;
        }
    }
}

HandleKeys.h:

#ifndef __HANDLEKEYS_H__
#define __HANDLEKEYS_H__

extern void HandelKey(SDL_Event e);
#endif

Make file output:

g   Main.cpp Modules/HandleKeys.cpp -w -lSDL2 -o Build/build.deb
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data 0x0): multiple definition of `SCREEN_WIDTH'; /tmp/ccdTvusg.o:(.data 0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data 0x4): multiple definition of `SCREEN_HEIGHT'; /tmp/ccdTvusg.o:(.data 0x4): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data.rel.local 0x0): multiple definition of `title'; /tmp/ccdTvusg.o:(.data.rel.local 0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.bss 0x0): multiple definition of `EntityPaths[abi:cxx11]'; /tmp/ccdTvusg.o:(.bss 0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o: warning: relocation against `PlayerSpeed' in read-only section `.text'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccdTvusg.o: in function `main':
Main.cpp:(.text 0x3e0): undefined reference to `HandelKey(SDL_Event)'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o: in function `HandleKey(SDL_Event)':
HandleKeys.cpp:(.text 0x49): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text 0x5f): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text 0x75): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text 0x8b): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
make: *** [Makefile:23: all] Error 1

i have messed around with using the "extern" parameter but nothing relay works, i have also messed around with how i define things in both global.h and main.cpp.

I am on pop OS (a Ubuntu based Linux distro).

Edit: I added the wrong main.cpp (I Had another open in my vscode workspace!)

Edit2: I now have a new error:

/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccwvE6bo.o: in function `main':
Main.cpp:(.text 0x3e0): undefined reference to `HandelKey(SDL_Event)'
collect2: error: ld returned 1 exit status
make: *** [Makefile:23: all] Error 1

CodePudding user response:

The lines

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

in Main.cpp and the lines

extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;

in Global.h are definitions. A declaration with an initializer is always a definition.

By defining the same variable in more than one translation unit (i.e. .cpp file), you are violating the one-definition rule.

I suggest that you change

extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;

in Global.h to the following:

extern const int SCREEN_WIDTH;
extern const int SCREEN_HEIGHT;

That way, it is only a declaration and no longer a definition.

You have a similar problem with title defined in Global.h:

extern char* title = "Top Down";

This line is a definition.

Header files should generally only contain declarations of variables, not definitions, because otherwise, if the header files is included by more than one translation unit, it will violate the one-definition rule.

Therefore, I suggest that you change this line to a declaration:

extern const char* title;

In one of the translation units, for example Main.cpp, you should then provide the definition:

const char* title = "Top Down";

The variable EntityPaths has a similar problem:

extern list<string> EntityPaths
{
    "Assets/Player.bmp",
};

This is a definition. You should not define it in the header file. Instead, you should only provide a declaration:

extern list<string> EntityPaths;

You should define it in exactly one translation unit, for example in Main.cpp.

In Global.h, you have declared PlayerSpeed:

extern const int PlayerSpeed;

However, you did not define it anywhere. I suggest that you add

const int PlayerSpeed;

to Main.cpp.

The line

extern void HandelKey(SDL_Event e);

in HandleKeys.h has a typo. It should be HandleKey, not HandelKey.

  • Related