1

I have just started to use C++ and SDL, and I have begun to create a basic Breakout clone to get my head around the semantics. To modularise what I have so far, I have created two header files core.h and paddle.h.

I am having difficulty with correctly including SDL with these modules. Initially, the project compiled when all I had was the main file breakout.cpp, paddle.h and paddle.cpp. At this stage, the 'Core' class was located in breakout.cpp, and as soon as I migrated it into its own file, the compiler started to get upset.

As it stands, it's a very simple set up that can be illustrated as follows:

breakout ---> core ---> paddle

Which leads me to believe that I am making a rookie oversight. Or that I'm linking the files incorrectly in Makefile.

Here is the code:

breakout.cpp

#include "SDL2/SDL.h"
#include "SDL2/SDL_image.h"
#include <stdio.h>
#include <string>
#include "core.h"

int main(int argc, char* args[]) {
    Core gCore;
    gCore.runGame();

    return 0;
}

core.h

#pragma once

#include "paddle.h"

class Core {
public:
    Core();
    ~Core();
    void runGame();

private:
    static const int SCREEN_WIDTH = 640, SCREEN_HEIGHT = 480;

    SDL_Window* gWindow;
    SDL_Renderer* gRenderer;

    Paddle* p1;

    void render();
};

paddle.h

#pragma once

#include <string>

class Paddle {

    friend class Core;

public:
    Paddle( SDL_Renderer* gRenderer );
    ~Paddle();

    void free();

private:
    static const int VELOCITY = 5;
    int xPos, yPos, pWidth, pHeight;

    SDL_Texture* pSprite;
    SDL_Rect* pRect;

    bool loadFromFile( SDL_Renderer* gRenderer, std::string path );

    int getXPos();  int getYPos();
    int getWidth(); int getHeight();
};

core.cpp

#include "SDL2/SDL.h"
#include "SDL2/SDL_image.h"
#include <stdio.h>
#include "paddle.h"
#include "core.h"

Core::Core() {

    // Set up SDL
    if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
        printf( "SDL could not initialise! SDL Error: %s\n", SDL_GetError() ); 
    }
    else {
        if ( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) ) {
            printf( "Warning: Linear texture filtering not enabled!" );
        }
        else {
            gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT,
                                        SDL_WINDOW_SHOWN );
            if ( gWindow == NULL ) {
                printf( "Could not create window. SDL Error: %s\n", SDL_GetError() ); 
            }
            else {
                gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
                if ( gRenderer == NULL ) {
                    printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
                }
                else {
                    // Initialise renderer colour
                    SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );

                    // Initialise PNG loading
                    int imgFlags = IMG_INIT_PNG;
                    if ( !( IMG_Init( imgFlags ) & imgFlags ) ) {
                        printf( "SDL_image could not be initialised! SDL_image Error: %s\n", IMG_GetError() );
                    }
                }
            }
        }
    }
}

Core::~Core() {
    SDL_DestroyRenderer( gRenderer );    gRenderer = NULL;
    SDL_DestroyWindow( gWindow );        gWindow = NULL;

    // Quit SDL subsystems
    IMG_Quit();
    SDL_Quit();
}

void Core::runGame() {
    // Main loop flag
    bool quit = false;

    // Event handler
    SDL_Event e;

    // p1 = new Paddle( gRenderer );

    while ( !quit ) {
        while( SDL_PollEvent( &e ) != 0 ) {
            //User requests quit
            if( e.type == SDL_QUIT ) {
                quit = true;
            }
        }

        // Clear screen
        SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
        SDL_RenderClear( gRenderer );

        // Render game assets
        render();

        // Update screen
        SDL_RenderPresent( gRenderer );
    }   
}

void Core::render() {
    // Set rendering space and render to screen
    SDL_Rect pRenderQuad = { p1->xPos, p1->yPos, p1->pWidth, p1->pHeight };

    // Render to screen
    SDL_RenderCopy( gRenderer, p1->pSprite, NULL, &pRenderQuad );
}

paddle.cpp

#include "SDL2/SDL.h"
#include "SDL2/SDL_image.h"
#include <stdio.h>
#include <string>
#include "paddle.h"

Paddle::Paddle(SDL_Renderer* gRenderer) {

    xPos =   300;  yPos =    400;
    pWidth =   0;  pHeight =   0;

    loadFromFile( gRenderer, "paddle.png" );
}

Paddle::~Paddle() {
    free();
}

bool Paddle::loadFromFile(SDL_Renderer* gRenderer, std::string path) {
    // Get rid of preexisting texture
    free();

    SDL_Texture* nTexture = NULL;

    SDL_Surface* lSurface = IMG_Load( path.c_str() );
    if ( lSurface == NULL ) { printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() ); }
    else {
        nTexture = SDL_CreateTextureFromSurface( gRenderer, lSurface );
        if ( nTexture == NULL ) { printf( "Unable to load texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() ); }
        else {
            pWidth = lSurface->w;
            pHeight = lSurface->h;
        }

        SDL_FreeSurface( lSurface ); // Surface no longer needed
    }

    pSprite = nTexture;
    return pSprite != NULL;
}

void Paddle::free() {
    if ( pSprite != NULL ) {
        SDL_DestroyTexture( pSprite );
        pWidth = 0; pHeight = 0;
    }
}

// Getter methods
int Paddle::getXPos() { return xPos; }      int Paddle::getYPos() { return yPos; }
int Paddle::getWidth() { return pWidth; }   int Paddle::getHeight() { return pHeight; }

And I have also included the Makefile, as there could easily be something wrong there too.

Makefile

OBJS = breakout.cpp
DEPS = paddle.h core.h

CC = g++

COMPILER_FLAGS = -w

LINKER_FLAGS = -lSDL2 -lSDL2_image

OBJ_NAME = breakout

%.o: %.cpp $(DEPS)
    $(CC) -c -o $@ $< $(COMPILER_FLAGS)

all : $(OBJS)
    $(CC) $(OBJS) $(COMPILER_FLAGS) $(LINKER_FLAGS) paddle.cpp core.cpp -o $(OBJ_NAME)

Error log

g++ breakout.cpp -w -lSDL2 -lSDL2_image paddle.cpp core.cpp -o breakout
/tmp/cc0H2fKM.o: In function `Paddle::loadFromFile(SDL_Renderer*, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
paddle.cpp:(.text+0x111): undefined reference to `IMG_Load'
paddle.cpp:(.text+0x121): undefined reference to `SDL_GetError'
paddle.cpp:(.text+0x15a): undefined reference to `SDL_CreateTextureFromSurface'
paddle.cpp:(.text+0x16a): undefined reference to `SDL_GetError'
paddle.cpp:(.text+0x1b8): undefined reference to `SDL_FreeSurface'
/tmp/cc0H2fKM.o: In function `Paddle::free()':
paddle.cpp:(.text+0x203): undefined reference to `SDL_DestroyTexture'
/tmp/ccDHGaLY.o: In function `Core::Core()':
core.cpp:(.text+0x12): undefined reference to `SDL_Init'
core.cpp:(.text+0x1e): undefined reference to `SDL_GetError'
core.cpp:(.text+0x44): undefined reference to `SDL_SetHint'
core.cpp:(.text+0x86): undefined reference to `SDL_CreateWindow'
core.cpp:(.text+0xa1): undefined reference to `SDL_GetError'
core.cpp:(.text+0xd1): undefined reference to `SDL_CreateRenderer'
core.cpp:(.text+0xee): undefined reference to `SDL_GetError'
core.cpp:(.text+0x127): undefined reference to `SDL_SetRenderDrawColor'
core.cpp:(.text+0x138): undefined reference to `IMG_Init'
core.cpp:(.text+0x149): undefined reference to `SDL_GetError'
/tmp/ccDHGaLY.o: In function `Core::~Core()':
core.cpp:(.text+0x17a): undefined reference to `SDL_DestroyRenderer'
core.cpp:(.text+0x195): undefined reference to `SDL_DestroyWindow'
core.cpp:(.text+0x1a5): undefined reference to `IMG_Quit'
core.cpp:(.text+0x1aa): undefined reference to `SDL_Quit'
/tmp/ccDHGaLY.o: In function `Core::runGame()':
core.cpp:(.text+0x1ea): undefined reference to `SDL_PollEvent'
core.cpp:(.text+0x218): undefined reference to `SDL_SetRenderDrawColor'
core.cpp:(.text+0x228): undefined reference to `SDL_RenderClear'
core.cpp:(.text+0x244): undefined reference to `SDL_RenderPresent'
/tmp/ccDHGaLY.o: In function `Core::render()':
core.cpp:(.text+0x2d1): undefined reference to `SDL_RenderCopy'
collect2: ld returned 1 exit status
make: *** [all] Error 1

I'd would really appreciate your support on not just the problem at hand. But also if any general suggestions about my code can be made.

Thanks

manderc3
  • 69
  • 2
  • The linker does not find the function in the SDL libraries. This could be caused by the order of arguments to the linker, try placing the `LINKER_FLAGS` after the .cpp files in your Makefile. – Karsten Koop Apr 26 '17 at 09:40
  • 2
    `-w`? *Why?* -- – Quentin Apr 26 '17 at 10:05
  • Thank you for the suggestion Karsten Koop, it fixed the problem immediately! And Quentin, using a warning removal flag is certainly a bad idea. The inclusion of that flag was a direct consequence from following a certain SDL tutorial. – manderc3 Apr 26 '17 at 10:28

1 Answers1

3

The order of the flags on your compiler switch matter.

Libraries to be passed to the linker should be last.

And using a -w makes no sense as it's usually -W (capital) followed by some kind of directive, such as -Werror -Wall etc.

Suggesto: remove -w flag and move the -l switches to the end.

Martein Txz
  • 178
  • 7
  • 1
    Not exactly makes no sense - just far worse than not having it at all (`-w` is disable-all-warnings in gcc). – keltar Apr 26 '17 at 12:49