Is my main.cpp good or did I over do it?

Started by
14 comments, last by Cygon 18 years, 5 months ago
Quote:Original post by deathwearer
Quote:Original post by agi_shi
Quote:Original post by superpig
Why not move all that initialisation out of the constructor and into something like CGameManager::Initialise()? That way you could return true or false directly, and you wouldn't need the extra status variable.


A fake init() function? That's what the constructor is for, initialization! And I need m_Status to return in main, so I don't return any ugly 0's. Plus I can use m_Status as a check, too.


The constructor is more used to Init your attributes (not sure of the english word) to their starting values. e.g m_bGameRunning = true;

The SDL Initialisation should be in a function because this way it can return if the Init was succesfull or no.


Initializers lists are for that.
Advertisement
Throw an exception if something goes wrong in the constructor. Since that's obviously not supposed to happen, it's a valid use of the exception (assuming you can make the ctor exception-safe). Also, the status is *not* going to be "failed" after the main loop ends - if it's possible for something seriously wrong(TM) to happen during that loop, you could throw an exception from there, too. That gets rid of the need for GetStatus() entirely (and it was buggy: (a) it gets called twice at the beginning and we don't know right now that it doesn't have side effects; (b) the return from main is supposed to be 0, i.e. a 'false' value, if everything went ok. The reasoning on that one is that there is only one way for everything to be ok and multiple ways to fail, so the unique 0 value is used for success and non-zero values for error codes. Also sprach POSIX.)

Also, I'd give the game constructor a chance to look at the command-line arguments - you may want that later :)

#include <vector>#include <string>#include <exception> // or should it be <stdexcept>?#include <iostream>using namespace std;int main(int argc, char *argv[]) {  vector<string> arguments(argv, argv + argc); // I think that's right  try {    // I vote against Hungarian notation and I especially hate the 'C'    // prefix for classes. Also, this instance is *not* a global.    GameManager mainGame(arguments);    while (mainGame.doLoop()); // why complicate the interface more than that?  } catch (exception& e) {    cout << "OH NOES!!!11 " << e.what() << endl;    return -1; // some sort of error.    // You could use your own exception class and give it a .errorCode()    // function or something.  }  // 0 is returned implicitly here if no exception happened.}
That's roughly similar to many of our game main functions. There is an application class that handles all the good stuff and main basically instantiates it, inits it, and then runs or calls its game loop. Occasionally we have to add a few more lines of crap in there for various technologies we need to use and so on.

At any rate, if you encapsulate everything well into your game manager, it's all good.
Quote:Original post by Zahlman
Throw an exception if something goes wrong in the constructor. Since that's obviously not supposed to happen, it's a valid use of the exception (assuming you can make the ctor exception-safe). Also, the status is *not* going to be "failed" after the main loop ends - if it's possible for something seriously wrong(TM) to happen during that loop, you could throw an exception from there, too. That gets rid of the need for GetStatus() entirely (and it was buggy: (a) it gets called twice at the beginning and we don't know right now that it doesn't have side effects; (b) the return from main is supposed to be 0, i.e. a 'false' value, if everything went ok. The reasoning on that one is that there is only one way for everything to be ok and multiple ways to fail, so the unique 0 value is used for success and non-zero values for error codes. Also sprach POSIX.)

Also, I'd give the game constructor a chance to look at the command-line arguments - you may want that later :)

#include <vector>#include <string>#include <exception> // or should it be <stdexcept>?#include <iostream>using namespace std;int main(int argc, char *argv[]) {  vector<string> arguments(argv, argv + argc); // I think that's right  try {    // I vote against Hungarian notation and I especially hate the 'C'    // prefix for classes. Also, this instance is *not* a global.    GameManager mainGame(arguments);    while (mainGame.doLoop()); // why complicate the interface more than that?  } catch (exception& e) {    cout << "OH NOES!!!11 " << e.what() << endl;    return -1; // some sort of error.    // You could use your own exception class and give it a .errorCode()    // function or something.  }  // 0 is returned implicitly here if no exception happened.}


That would just change my code entirely.

How the heck is one variable 'buggy'? It's true. Then, say something fails. It becomes false. The constructor ends before you know it. You check for falsity. Yes, ehh? You end main(). That's it.

Passing main()'s arguments to the constructor may be needed, but if you think about it realy realy hard, you can always provide some other option to change options since I'm not going to run the program from the command line every single time.

Since when was main() needed to return 0? What main() returns is useful for other programs, but nothing else. And if other programs need my program's return code then I wrote them. All I could do then is check if main was fasle or true. Much, much, easier.
Quote:Original post by agi_shi
Since when was main() needed to return 0?

Since always. Its a long standing standard, and it is generally bad form to write off standards just because you don't see the point. This particular standard is even promoted by the language itself...if the program reaches the end of main without returning anything, valid execution is assumed and 0 is returned implicitly.

CM
I don't think a file can be 'too short' in C++ ;)

Creating your CGameManager on the stack is a good idea, if you ask me. That way, your destructor can perform cleanup work in case an exception ends the game.

My main tends to look like this:
int main(int argc, const char *argv[]) {  using namespace std;  try {      // Run a new instance of our game    MyGame(vector<string>(argv, argv + argc)).run();  }  catch(const exception &e) {    cerr << e.what() << endl;    #ifndef NDEBUG    // In debug mode, give the debugger a chance to break    throw;     #endif    return -1;  }  return 0;}


My applications always have a class with the name of the application itself that have a run() method or an overloaded function operator. Therein you will find the main loop, or whatever is needed to keep the application alive.

-Markus-
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.

This topic is closed to new replies.

Advertisement