Organizing a Medium/Large C++ Project & When to Use Try/Catch?

Broad Project Goal / Idea Behind It:
The user plays a game where they have a market which buys and sells different items for different prices and amounts. For example, someone could walk into their shop and buy 32 stone for $10. Now, players can also sell (to the shop) 32 stone for $20. This creates an exploit that allows players to double their money with no real work and the shop owner will lose money.

The program I'm making will have a user input their shop data, then do some calculations, and point out any exploits that are possible in their shop.

To do this project, I must be able to:
- Organize my code in such a way to allow easily adding to it / modifying it later in time.
- Parse JSON files and store that data into my program. I plan to use JsonCPP which is an open source JSON parser on Github.
- Etc..

Basic Structure Behind Project:
- take user input or a file
- store data into program (using a class)
- compare user's inputted data to the game's official data (in JSON files)
- then do some calculations
- output the result to the user

----------------------------
So now that you understand the overview I have two questions:

1. What is the best way to organize my code for the program? I am currently using 3 files:
a. Main.cpp (only has main() function)
b. functions.cpp (Has all functions)
c. functions.h (has Items class used in functions.cpp)

However, I'm starting to realize that functions.cpp is going to get very large and that'll probably make it pretty confusing.

So what's the best way to organize the files? For example, should I have a new file called jsonParser.cpp which has all functions related to parsing the JSON files and storing the data into the program? Then should I have a file called Item.cpp which has all functions for the Item.h (class) file?

I'm also currently using Visual Studio Code + Command Prompt / g++ so if I link files I have to type them in myself. For example:
-std=c++14 Main.cpp functions.cpp
./a.out
Meaning if I have a LOT of files, it becomes much more to type. The only other way I know how to compile programs is Visual Studio but it is significantly slower at compiling and running in comparison.

[Solved] 2. What is the point of exceptions? Or to be more clear, when should I use them? We touched on them in class last semester but I didn't really understand them. Now I'm learning about them through the textbook: "Programming: Principles and Practice Using C++" (2nd edition - Kindle version) but it doesn't explain when to use them. I also read: http://www.cplusplus.com/doc/tutorial/exceptions/

For example, in my code below, should I be using try blocks or should I just be using normal code (no exceptions)?
The Code Without Try Blocks: http://cpp.sh/9qblg
The Code With Try Blocks: http://cpp.sh/7dcoq
* Please Note: There is a bug so the catch blocks don't actually work.. I don't see what the problem is..



Help would be greatly appreciated, thank you!

Please note: I could either respond tomorrow or in a few days. It just depends how busy I am with my summer semester classes.
Last edited on
Your catch block isn't working because you're not throwing anything at it. Line 42 - you simply typed throw; - Even though catch(...) doesn't have a specific type of throw to catch, you still need to actually throw something.

simply make it throw 1; or throw "You Screwed Up!"; and it'll work.

The whole point of throwing exceptions is so you can handle them. For example, lets say you made a calculator. If the power inputs random letters, you don't want your program to crash or fail. Instead, check if the input is invalid. If so, throw an exception. However, there might be multiple reasons for the input to be wrong, such as letters, invalid operator, dividing by 0, etc.. So you'll have multiple cases where you'd want to throw an exception.

My calculator example isn't amazing, but as a program becomes more complex with more room for epic errors, you'll want to be able to catch them and handle them effectively.
zapshe wrote:
Your catch block isn't working because you're not throwing anything at it. Line 42 - you simply typed throw; - Even though catch(...) doesn't have a specific type of throw to catch, you still need to actually throw something.

simply make it throw 1; or throw "You Screwed Up!"; and it'll work.

The whole point of throwing exceptions is so you can handle them. For example, lets say you made a calculator. If the power inputs random letters, you don't want your program to crash or fail. Instead, check if the input is invalid. If so, throw an exception. However, there might be multiple reasons for the input to be wrong, such as letters, invalid operator, dividing by 0, etc.. So you'll have multiple cases where you'd want to throw an exception.

My calculator example isn't amazing, but as a program becomes more complex with more room for epic errors, you'll want to be able to catch them and handle them effectively.

Thank you for your response!

I now know I misread the tutorial (http://www.cplusplus.com/doc/tutorial/exceptions/) which is why I thought throw; would work.

Although I still don't fully understand why they're so useful. But probably because I haven't made any very complex programs.

For your calculator example, couldn't you just get the user's input, then with if statements and maybe for loops, check if the input has any issues right after they input the info then do your calculations?

Edit: I tried adding the 1 to throw 1; but it is still not working. Here's the new code: http://cpp.sh/457wb. To see what I'm seeing, run it and input something containing letters like: "v1.13"
Last edited on
C++ Shell doesn't seem to show the output to std::cerr anywhere. If I change to cerr to cout, or run it locally, it does show the message.
Last edited on
PiggiesGoSqueal wrote:
Although I still don't fully understand why they're so useful. But probably because I haven't made any very complex programs.

For your calculator example, couldn't you just get the user's input, then with if statements

You're on the right track: exceptions become increasingly useful as your program becomes more complex, because they simplify code.

Imagine when the input was taken from the user, the code makes a function call, that function makes another function call.. and 50 calls deep, there's finally a function that realizes that the input was invalid and it can't proceed.

It could do an if statement, return an error code, the caller would have to do an if statement, return the error code, etc. 50 levels of nothing but check for error and if there is one, undo any work done so far and return error to the caller. With exceptions, there is one throw where the error was detected, and one try/catch where the problem could be handled, with no extra code anywhere inbetween.

Real example:
When you open a file in Notepad++, it starts reading it, as it reads it, it calls functions that prepare lines of text for drawing, choose fonts, apply highlighting, etc, and then one of these functions, maybe not 50 but more than 10 levels deep, in the text manipulation library called scintilla, runs out of memory allocating another word to display. It throws the exception std::bad_alloc, which undoes everything done so far in that function, and in its caller, and in its caller, until it reaches the try/catch clause in the Notepad++ function that asked the user for the file name to open, and the catch clause pops up the message saying the file was too large to open.
Yes, odd. C++ Shell doesn't show the message if it's std::cerr but will if it's std::cout .

For your calculator example, couldn't you just get the user's input, then with if statements and maybe for loops, check if the input has any issues right after they input the info then do your calculations?

Yea, you could. Not my best example. However, for instance, look at something like this:

int* arr = new int[1000];

If "new" fails to allocate memory for this, it'll throw an exception. In order to catch an error from this, you have to use try and catch (or make it new(nothrow) but why bother).
Thank you for your replies everyone!

Peter87 wrote:
C++ Shell doesn't seem to show the output to std::cerr anywhere. If I change to cerr to cout, or run it locally, it does show the message.

Ah strange. You are correct. Thanks for pointing that out!

zapshe wrote:
Yea, you could. Not my best example. look at something like this:

int* arr = new int[1000];

If "new" fails to allocate memory for this, it'll throw an exception. In order to catch an error from this, you have to use try and catch (or make it new(nothrow) but why bother).

No worries! I appreciate the calc example regardless. :)

Now, how's the code below look? Although it doesn't throw anything. Not sure where I'd put the "throw" word.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int main() {
    try {
        int num = 1000;
        int *arr = new int[num]; // Where are you supposed to put "throw"?
        // Maybe Something Like: (?)
        //if (!(int *arr = new int[10000000])) throw 1;
    }
    catch (...) {
        std::cout << "Exception thrown! Could not allocate enough memory for new int[num]\n";
    }
    return 0;
}


Output:
9:22: warning: missing terminating " character 
9:9: error: missing terminating " character In function 'int main()': 
6:14: warning: unused variable 'arr' [-Wunused-variable] 
10:5: error: expected primary-expression before '}' token



Cubbi wrote:
Imagine when the input was taken from the user, the code makes a function call, that function makes another function call.. and 50 calls deep, there's finally a function that realizes that the input was invalid and it can't proceed.

It could do an if statement, return an error code, the caller would have to do an if statement, return the error code, etc. 50 levels of nothing but check for error and if there is one, undo any work done so far and return error to the caller. With exceptions, there is one throw where the error was detected, and one try/catch where the problem could be handled, with no extra code anywhere inbetween.

Real example:
When you open a file in Notepad++, it starts reading it, as it reads it, it calls functions that prepare lines of text for drawing, choose fonts, apply highlighting, etc, and then one of these functions, maybe not 50 but more than 10 levels deep, in the text manipulation library called scintilla, runs out of memory allocating another word to display. It throws the exception std::bad_alloc, which undoes everything done so far in that function, and in its caller, and in its caller, until it reaches the try/catch clause in the Notepad++ function that asked the user for the file name to open, and the catch clause pops up the message saying the file was too large to open.

Ahh okay, that is a very good example & explanation to help me see the difference. Thanks! Bookmarked this for future reference.
Last edited on
As the user of the code, you don't need to write "throw". You're using what's already been designed; that the new operator, by default, can throw std::bad_alloc.
Although it doesn't throw anything. Not sure where I'd put the "throw" word.

You yourself wont throw anything, it'll do that itself if the allocation fails for some reason. It'll throw "std::bad_alloc".

"new" will throw an exception if you try to allocate too much:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() 
{
	int num = 1000000000000;
	try
	{
		int *arr = new int[num];
	}
	
	//If allocation Fails
	catch (std::bad_alloc) 
	{
		std::cerr << "Exception Thrown! Could not allocate enough memory for new int[num]\n";
	}

	return 0;
}

Try it out yourself.

Catching the exception and handling it is a lot better than having your program crash.
Last edited on
Ganado wrote:
As the user of the code, you don't need to write "throw". You're using what's already been designed; that the new operator, by default, can throw std::bad_alloc.

Ah okay, thanks.

zapshe wrote:
"new" will throw an exception if you try to allocate too much:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() 
{
	int num = 1000000000000;
	try
	{
		int *arr = new int[num];
	}
	
	//If allocation Fails
	catch (std::bad_alloc) 
	{
		std::cerr << "Exception Thrown! Could not allocate enough memory for new int[num]\n";
	}

	return 0;
}

Try it out yourself.

Catching the exception and handling it is a lot better than having your program crash.

Ah okay, thanks for providing the example. Bookmarked your post for future reference. :)

Basic Structure Behind Project:
- take user input or a file
- store data into program (using a class)
- compare user's inputted data to the game's official data (in JSON files)
- then do some calculations
- output the result to the user

Usually at this point, the thread will likely die out as no one will answer the first question so I'll try to.

The best way to organize it is to make every one of those goals up there into it's own function for easier editing. In my opinion, using 3 files would making editing harder for smaller programs, since there are now 3 different files! If the program isn't that big, consider having it all in one .cpp . Unless they specifically told you to use different files, separate functions should be enough organizing I'd think.

Visual studio can be slow, but a good CPU paired with an SSD makes the wait time minimal. I myself have the main portion of the program on the SSD and other bigger parts of it installed on my HDD and it runs smoothly. Consider an upgrade perhaps.
zapshe wrote:
Usually at this point, the thread will likely die out as no one will answer the first question so I'll try to.

Haha, okay. Thank you for replying. :P

zapshe wrote:
The best way to organize it is to make every one of those goals up there into it's own function for easier editing. In my opinion, using 3 files would making editing harder for smaller programs, since there are now 3 different files! If the program isn't that big, consider having it all in one .cpp.

Thanks for your suggestions. I'll check out my code and consider doing some of that. :) I'm not sure what you consider to be a "medium" or "large" project, but for me personally, this is the largest project I've ever done in C++. I've merely scratched the surface of what I need to do haha. Bullet point 3 and 4 will be quite time consuming / difficult to figure out. But that's a good learning experience. :)

zapshe wrote:
Unless they specifically told you to use different files, separate functions should be enough organizing I'd think.

The program is for my free time not an assignment or job requirement. But it's by far the largest C++ project I've ever done I think, including my two classes (semesters) worth of C++ courses. The overview I included was just the broad & generalized overview.

zapshe wrote:
Visual studio can be slow, but a good CPU paired with an SSD makes the wait time minimal. I myself have the main portion of the program on the SSD and other bigger parts of it installed on my HDD and it runs smoothly. Consider an upgrade perhaps.

For Visual Studio I've used a laptop and desktop, both with great specs and it was still quite slow (in my opinion, at least in comparison to VSC).

The laptop has:
- 32 GB DDR4 RAM
- i7-8750H CPU @ 2.20-2.21GHz (6 cores)
- 400 GB SSD (OS is on SSD) + 1 TB HDD
- I use this laptop for most programming because I need to travel with it.

The Desktop has:
- 16 GB DDR4 RAM
- i7-8700k CPU
- 500 GB NvMe SSD (OS is on SSD) + 1 TB HDD
- I don't have access to this computer for the summer unfortunately.

I don't know. I may try using Visual Studio again. It's been about a semester since I last did. Do you know if it's possible to use flags for the compiling in Visual Studio? I recently learned to use these flags when compiling my C++ program:
-Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic


Again, thank you for your reply! :)
Last edited on
If it's your own project, then experiment and delve into it, find what you like to do and what feels best. A project's size and complexity usually determines if you need to go up and beyond with extra files. Use extra files and test things out. Either you'll like it or hate it. Maybe you'll hate it, try to make it into 1 file, then realize it was better the other way! See what you like and feel is more convenient.

And Visual Studio is a full blown IDE, so it'll be slower than VSC. Your setup seems to be more than enough. On my setup, Visual Studio usually only takes a few seconds to compile and run my code, 5 seconds at most I'd say. After you compile once, if you're simply editing your code, it'll only compile the changes and use the already compiled data from the last time to save time.

I know that there are ways to do things like flags in Visual Studio through the project settings, but I don't know much about them. I've never really had to mess with that. Only time I needed to use a flag was in my CS class since they use Linux. However, I doubt there's anything that those flags do that you can't do in Visual Studio.
The first thing for successfull programming is imho descriptive names. The next think is 'divide and conquer'.

For your project it would mean (roughly): Make a class game, player, shop, and may be item. Whether you make a file for each depends on the amount of code for each. You can make the file later when it grows.

With this structure you can start designing:
The game contains player/shop.
The shop contains item.
etc.

For json I suggest to use boost. More precisely property_tree:

https://www.boost.org/doc/libs/1_70_0/doc/html/property_tree.html

They provide parser for other formats. For initializing your program the INFO format is very good...

When to Use Try/Catch?
Don't. It's a nightmare when it comes to program flow.
1. What is the best way to organize my code for the program?

I usually organize project files like this:
Class1.h & Class1.cpp // files for Class1
Class2.h & Class2.cpp // files for Class2
...
misc.h & misc.cpp // miscellaneous functions that don't belong an a particular class
main.cpp

For your project, the classes might be Shop, Item and Parser.

if I have a LOT of files, it becomes much more to type.

If you're using g++ then you probably have make (or gmake) installed on your system. Create a makefile and all your troubles will go away.
zapshe wrote:
If it's your own project, then experiment and delve into it, find what you like to do and what feels best. A project's size and complexity usually determines if you need to go up and beyond with extra files. Use extra files and test things out. Either you'll like it or hate it. Maybe you'll hate it, try to make it into 1 file, then realize it was better the other way! See what you like and feel is more convenient.

Thanks, good idea! I just wanted to make sure there wasn't an official way or recommended way to organize larger projects.

zapshe wrote:
And Visual Studio is a full blown IDE, so it'll be slower than VSC. Your setup seems to be more than enough. On my setup, Visual Studio usually only takes a few seconds to compile and run my code, 5 seconds at most I'd say. After you compile once, if you're simply editing your code, it'll only compile the changes and use the already compiled data from the last time to save time.

Oh okay, I wasn't aware what the differences were between VS and VSC. Good to know! As for the compile time, maybe I was remembering the dreadfully slow times on the school computers haha. I'll try VS again to find out once I get it properly set up.

zapshe wrote:
I know that there are ways to do things like flags in Visual Studio through the project settings, but I don't know much about them. I've never really had to mess with that. Only time I needed to use a flag was in my CS class since they use Linux. However, I doubt there's anything that those flags do that you can't do in Visual Studio.

Okay, good to know! I managed to find where specifically online. I'll try to get it set up once I've figured out how to compile programs using WSL (Windows Sub Linux) rather than Windows. I'll follow a tutorial shortly for that.

And again, thank you for all of your responses! :)
------------------------
coder777 wrote:
The first thing for successfull programming is imho descriptive names. The next think is 'divide and conquer'.

Thanks for your reply! Yeah, I use descriptive names I think and comments where needed. Also I set up algorithm steps and break things down into parts. Boy am I glad my professor recommended / required us to do that. :P

coder777 wrote:
For your project it would mean (roughly): Make a class game, player, shop, and may be item. Whether you make a file for each depends on the amount of code for each. You can make the file later when it grows.

With this structure you can start designing:
The game contains player/shop.
The shop contains item.
etc.

Apologies if my explanation wasn't very clear, it was hard to explain the general / broad view of my project. Basically I wouldn't create a class for anything but Item. But that's okay! I understand what you meant, in other words, how to best structure it.

coder777 wrote:
For json I suggest to use boost. More precisely property_tree:

https://www.boost.org/doc/libs/1_70_0/doc/html/property_tree.html

They provide parser for other formats. For initializing your program the INFO format is very good...

Thanks for the suggestion! Hmm, I'm already following a tutorial for:
https://www.codeproject.com/Articles/1102603/Accessing-JSON-Data-with-Cplusplus#_articleTop
(a different JSON parser). But if I can't figure it out I'll consider using boost's. Also, why do you suggest using the INFO parser rather than the JSON parser? I am parsing JSON files after all. :P Unless I'm misunderstanding what you meant.

coder777 wrote:
When to Use Try/Catch?
Don't. It's a nightmare when it comes to program flow.

Haha, well, now I've been told opposite suggestions. :P Idk, I think I'll give it a try if I come across a situation that would require that std::bad_alloc (or something) setting. But other than that I probably won't use it.
------------------------
dhayden wrote:
1. What is the best way to organize my code for the program?


I usually organize project files like this:
Class1.h & Class1.cpp // files for Class1
Class2.h & Class2.cpp // files for Class2
...
misc.h & misc.cpp // miscellaneous functions that don't belong an a particular class
main.cpp

First, thanks for your reply! Second, that's a good idea. To clarify, would Class1.h have the actual Class declaration and member function references like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef CLASS1_H
#define CLASS1_H

#include <iostream>

class Item {
    private:
        // Declaring values & setting defaults.
        std::string name; // The item's name.
        double buy; // buy FROM market price
        double sell; // sell TO market price
        int amount; // Amount that the buy & sell prices are for.
        // Etc..

    public:
        // default constructor
        Item();

        // Overload constructor
        Item(const std::string newName, double newBuy, double newSell, int newAmount, bool newEnabledSetting);

        // Etc.... But only referencing the member functions not including their contents.
#endif 

Then would Class1.cpp have all of the function definitions for the class? That's the way my professor showed us when he introduced us to classes and header files last semester. But that almost seems backwards to me, because the .cpp file is the main file (out of that and the .h file) yet it is holding the details for the class, not the main part of the class (where it is declared).

Apologies if I'm not making sense. If not, you can just ignore that. :P

dhayden wrote:
if I have a LOT of files, it becomes much more to type.


If you're using g++ then you probably have make (or gmake) installed on your system. Create a makefile and all your troubles will go away.

I typed "make" in bash and it worked. gmake did not. Could you explain how "a makefile" will make all my troubles go away? Apologies for my lack of knowledge with Linux. Thanks!
Last edited on
Regarding Class1.h and Class1.cpp, you have it right. It's important to separate the declaration of the class from the definition of the methods because you might need to #include the .h file with the declaration in many other files. If you had the definitions in the header file then you'd end up with multiply defined methods.

Regarding make and makefiles, google "makefile tutorial" and see how to create a makefile. The idea is that if you properly setup the makefile, then after modifying any of the code, you just have to type "make" and it will recompile only what must be recompiled.
dhayden wrote:
It's important to separate the declaration of the class from the definition of the methods because you might need to #include the .h file with the declaration in many other files. If you had the definitions in the header file then you'd end up with multiply defined methods.

Ohh, I didn't think about that. Thank you for explaining that!

dhayden wrote:
Regarding make and makefiles, google "makefile tutorial" and see how to create a makefile. The idea is that if you properly setup the makefile, then after modifying any of the code, you just have to type "make" and it will recompile only what must be recompiled.

Oh okay, that sounds very handy! Also, should I be using cmake? I am still looking into it, but I've read it's cross platform and preferably I'd like everything (or most things) I make to be cross platform if I plan to let others use them. When I say cross platform I mean I want the end results, the finished product to be.
Last edited on
Also, why do you suggest using the INFO parser rather than the JSON parser? I am parsing JSON files after all.
I do not suggest using the INFO parser for your particular need. But it is useful for other things like initial data for your program.

The point is that all these formats leads to one associative container: ptree, which is easy to deal with.

Haha, well, now I've been told opposite suggestions. :P Idk, I think I'll give it a try if I come across a situation that would require that std::bad_alloc (or something) setting. But other than that I probably won't use it.
Actually the nature of exception handling is jump out of the middle of a function into the middle of another function more or less unexpectedly. That makes it hard to impossible to follow the program flow. It means: The fewer exceptions the more stable your program will be.
coder777 wrote:
I do not suggest using the INFO parser for your particular need. But it is useful for other things like initial data for your program.

The point is that all these formats leads to one associative container: ptree, which is easy to deal with.

Haha, well, now I've been told opposite suggestions. :P Idk, I think I'll give it a try if I come across a situation that would require that std::bad_alloc (or something) setting. But other than that I probably won't use it.

Actually the nature of exception handling is jump out of the middle of a function into the middle of another function more or less unexpectedly. That makes it hard to impossible to follow the program flow. It means: The fewer exceptions the more stable your program will be.

Ah okay. Also, apologies for late reply. I have school Monday - Thursday (generally) and program Friday - Sunday.

Well, I think I'll hold off on try & catch for now as it sounds way too complicated to make it worthwhile for what I'm working on now haha.

Thanks for replying. Post solved. :)
Last edited on
Topic archived. No new replies allowed.