0

I've been programming in C++ with VS 2010 Professional but I am stuck on this problem:

If the program starts and the connection is good then it loads the "gameactive" loop, and displays the high score of the player.

But when the connection has a error it freezes and crashes. I want to display a error like "error connecting to server" and continue loading the game.

I did use this tutorial: http://r3dux.org/2010/11/how-to-use-mysql-connectorc-to-connect-to-a-mysql-database-in-windows/

Here is my code:

// Standad C++ includes
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
#include <string.h>

// Include the Connector/C++ headers
#include "cppconn/driver.h"
#include "cppconn/exception.h"
#include "cppconn/resultset.h"
#include "cppconn/statement.h"
#include <cgl\cgl.h>
#include <cgl\core.h>
#include <core\corefile.h>

// Link to the Connector/C++ library
#pragma comment(lib, "mysqlcppconn.lib")

// Specify our connection target and credentials
const string server   = "tcp://xxx.xxx.xxx.xxx:3306";
const string username = "xxxxxxx";
const string password = "XXxxXXxxXXxx";

char myoutput[1024];
char myoutput1[1024];
char myoutput2[1024];

s_font font;
s_bitmap bmp_background;

int connectsetup()
{
    sql::Driver     *driver; // Create a pointer to a MySQL driver object
    sql::Connection *dbConn; // Create a pointer to a database connection object
    sql::Statement  *stmt;   // Create a pointer to a Statement object to hold our SQL commands
    sql::ResultSet  *res;    // Create a pointer to a ResultSet object to hold the results of any queries we run

    // Try to get a driver to use to connect to our DBMS
    try
    {
        driver = get_driver_instance();
    }
    catch (sql::SQLException e)
    {
        cout << "Could not get a database driver. Error message: " << e.what() << endl;
    }

    // Try to connect to the DBMS server
    try
    {
        dbConn = driver->connect(server, username, password);
    }
    catch (sql::SQLException e)
    {
        cout << "Could not connect to database. Error message: " << e.what() << endl;
    }

    stmt = dbConn->createStatement(); // Specify which connection our SQL statement should be executed on

    // Try to query the database
    try
    {
        stmt->execute("USE runner");              // Select which database to use. Notice that we use "execute" to perform a command.

        res = stmt->executeQuery("SELECT * FROM highscores"); // Perform a query and get the results. Notice that we use "executeQuery" to get results back
    }
    catch (sql::SQLException e)
    {
        cout << "SQL error. Error message: " << e.what() << endl;
    }

    // While there are still results (i.e. rows/records) in our result set...
    while (res->next())
    {
        // ...get each field we want and output it to the screen
        // Note: The first field/column in our result-set is field 1 (one) and -NOT- field 0 (zero)
        // Also, if we know the name of the field then we can also get it directly by name by using:
        // res->getString("TheNameOfTheField");
        //printf(myoutput,"%s %s %s",res->getString(1),res->getString(2),res->getString(3));
        strcpy(myoutput,res->getString(1).c_str());
        strcpy(myoutput1,res->getString(2).c_str());
        strcpy(myoutput2,res->getString(3).c_str());
    }

    // Clean up after ourselves
    delete res;
    delete stmt;
    delete dbConn;

    return 0;
}

void coremain()
{
    //Fullscreen, windowed of scaled?
    corefile_mountimage("res",MOUNT_DIR);
    CGL_InitVideo(1280, 720, CGL_VIDEO_NONE);
    CGL_SetTitle("CGL - Endless poepert");
    CGL_InitFont("font_heat.tga", &font);
    CGL_LoadBitmap("track1.tga",&bmp_background);
    connectsetup();
    int gameactive=1;

    int getal=atoi(myoutput);

    do {
        do {
            CGL_WaitRefresh();
            CGL_DrawBitmap(0,0,bmp_background); 
            CGL_DrawCenteredText(100,font, "%s: %d van %s score %s",myoutput,getal,myoutput1,myoutput2);
            int key,keytrig;
            CGL_GetKeys(&key,&keytrig);
            if (keytrig & CGL_INPUT_KEY_EXIT) exit(EXIT_SUCCESS);
            CGL_SwapBuffers(); 
        } while(gameactive);
        CGL_FlushGraphics();

    } while(1);
    CGL_CloseVideo();
}
nobody
  • 19,814
  • 17
  • 56
  • 77
  • 4
    It's not C++, it's your program that is crashing :-) – Sergey Kalinichenko May 13 '14 at 15:59
  • 1
    Where does it crash? Attach a debugger and see what line is crashing. – Donnie May 13 '14 at 16:00
  • it compiles and works... – user3633246 May 13 '14 at 16:02
  • Just because it compiles doesn't mean it's correct or that it will work. Can you post whatever error you're getting? Amend your question with an edit to avoid cluttering the comments. – tadman May 13 '14 at 16:03
  • when running with false server settings it gives this error: Run-Time Check Failure #3 - The variable 'dbConn' is being used without being initialized. – user3633246 May 13 '14 at 16:03
  • 4
    Your `try`/`catch` structure here is totally messed up. You're catching and **ignoring** errors that should actually be a hint to stop instead of blindly continuing down the path of using a bunch of uninitialized variables. Remember, signs like **BRIDGE OUT AHEAD** means **stop driving**. – tadman May 13 '14 at 16:04
  • Your `connectSetup` function seems to not even care that there is a problem. Sure, you have try/catch blocks, but you don't do anything to stop the execution inside that function. – PaulMcKenzie May 13 '14 at 16:04
  • how do i stop the execution and continue to the program? – user3633246 May 13 '14 at 16:07
  • @user3633246 - How about returning a value denoting the error? Or throw an exception back up to the caller. If you can't open the database, why continue to attempt to read from an unopened db handle? – PaulMcKenzie May 13 '14 at 16:08
  • because the game can be played without using the highscore system... if the connection is down you can play but can submit your score... – user3633246 May 13 '14 at 16:10
  • 1
    So why didn't you return immediately on failure of the opening of the database? Did you not know that you can issue a `return` in the middle of the function? Also, it would be a good time to learn RAII http://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii, so that the return will clean up anything that *was* created, but no longer needed. – PaulMcKenzie May 13 '14 at 16:13
  • if i add a return 0; to all the catch... i get this error: Run-Time Check Failure #2 - Stack around the variable 'e' was corrupted. – user3633246 May 13 '14 at 16:16
  • I think you need to look at my answer, and see if that helps you any. In addition, why are you using char arrays and `strcpy()`? – PaulMcKenzie May 13 '14 at 16:43

1 Answers1

0

The issue seems to be your connectSetup function. You have try/catch blocks, but you don't do anything if there is an issue. You just keep going as if nothing is wrong.

Also, this is a great time to learn smart pointers. The reason why smart pointers should be used here is in the case where something goes wrong even if the database connection is successful (you have subsequent try/catch blocks). You want to ensure that anything that was allocated is cleaned up, regardless of when or where you issue a return statement.

#include <memory>
#include <string>

std::string myoutput;
std::string myoutput1;
std::string myoutput2;

int connectsetup()
{
   sql::Driver* driver;
   std::unique_ptr<sql::Connection> dbConn;
   std::unique_ptr<sql::Statement> stmt;
   std::unique_ptr<sql::ResultSet> res;

   try
   {
        driver = get_driver_instance();
        dbConn.reset(driver->connect(server, username, password))
        stmt.reset(dbConn->createStatement());         
        stmt->execute("USE runner"); 
        res.reset(stmt->executeQuery("SELECT * FROM highscores")); // Perform a query 
        while (res->next())
        {
            myoutput = res->getString(1);
            myoutput1 = res->getString(2);
            myoutput2 = res->getString(3);
        }
    }
    catch (const sql::SQLException& e)
    {
        cout << "Something went wrong with the database stuff.  Here it is: " << e.what() << endl;
        return -1;
    }
    return 0;
}

This code was not compiled, but I attempted to extract what your original code was doing and rewrote it using smart pointers.

The code above will throw an exception if any of those statements fail. Note that we catch the exception by reference, not by value. Also note that there is no need for delete, since std::unique_ptr does this work automatically.

Finally, there is no need for char arrays -- why did you introduce them? It only made another part of your function vulnerable to a memory overwrite, namely in the res->next() loop.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45