Hello again..
I guess you are all tired of my questions, but I ask them to understand
This is my code, so you can take a look at it:
http://pastebin.com/vMHCTBu9
As you can see the code is very ugly and not even understandable, if not for all the comments.
I plan to use some sort of GUI Library like aGUI when I have researched it a bit more and read the documentation.
But anyways, here's my question:
How would I for example, simplify this code to make my life easier?
I've read about Object Oriented Programming and it should help make my life easier
So what I really need help with is;
How would I go by adding OOP to say my buttons, in a way that it's structured?
Any help is much appreciated, and would be splendid if you could give me examples
OOP will help you organize your code, and make autonomous objects.
Ex.
class Button { int x,y,w,h; ALLEGRO_BITMAP* img; bool IsMouseOver(int msx,int msy); void Draw(); };
Even something as simple as that will help you shorten your code. And it will help get rid of hard coded values, which are hard to modify.
Or, you could start by learning how to walk before you try to run and learn how to use functions to structure your program. Normally you'd have a function to initializing everything, one for cleaning up on closing down, and one for the main loop. Then the main loop function calls one function for drawing and one for input handling, and one for AI. And so on as is needed. I think OOP is a bit overrated. I wouldn't study it before learning how to use all the basics of the C language.
I guess you are all tired of my questions,
Not at all.
Object-oriented is just a way of organizing things, of which there are many ways. Objects are collections of data, and that data can only be accessed by that object's methods. That way, if you know you wrote the set and get functions correctly (to plan for unsafe/erroneous inputs), you know for a fact that that object will never have corrupted data inside it.
Basically, it's just a standard way of structuring things to keep your data from being accessed in ways you never intended. Encapsulation.
As you can see the code is very ugly and not even understandable, if not for all the comments.
Not really. But you're incorrectly using elseif, instead of a switch statement or just a bunch of if statements. Look into those.
You also want your code in modular functions. That means you put all your statements initializing allegro in say, a function called initialize_game(). You can also do one for handle_events(), game_logic(), and draw_a_frame().
Something else OOP will do for you is get rid of globals. Both beoran and Katko are suggesting a design that will necessitate global data or dependency injection (passing everything around in a giant clusterfsck).
What is really needed is a way for you to organize your data into relevant objects. In a class, all member objects are global to class methods. But they are only as accessible as you make them (private , protected , public).
play_game() becomes Game::Play().
or dependency injection (passing everything around in a giant clusterfsck).
Those aren't really related. you can still group data into structs, and not be OO. But Dependency Injection is usually the way to go.
Wow, thank you for all your answers.
As for the switch statements Chris, I know I should have used them, but I was just testing if the code worked with if statements. If statements are easier in my opinion, so testing code with those are just the way I go at first. Then after that I tend to change them to switch statements if they are needed
Anyways, I am having troubles for example when creating a function to initialize everything.
In the Main function it will have errors like;
Error: identifier "display" is undefined, etc..
I will look into ways to organize my program better; Functions, Objects etc..
Thank you all
Edgar, how does OOP not lead to dependency injection? The data must go somewhere no matter if it's on the stack heap or in the bss. In this aspect classes are equivalent to using structs + functions. And "global" data is fine if you hide it behind a proper functional interface. Much like Allegro does.
OOP encourages you not to use global data and dependency injection, but rather to group your data in the most effective ways. Yes there will still be data getting passed around, because not every object can know everything it will ever need (ie. not all the data will / should be members of the class).
Well, it's true that it's better to organize data thematically, but you can do that perfectly with C structs. And with OOP and no global data, you can't avoid having a "god object" that holds the references to all long-lived data that exists in your program either directly or recursively, or a list of such objects in your main() function.
In contrast, if you let your .c files hang on to their own long lived "global" data, but put a clean function API in front of it, you get much better modularity than with such a "god object" data hierarchy. Just think of, say, OpenGL. All you get are integer handles, all the global OpenGL state is in the GPU, safely abstracted away behind a function based interface.
Also, C is excellent for encapsulation with integer handles, or if you use incomplete struct declarations. In the header player.h header you put typedef struct Player_ Player; and all getter and setter functions, like, say `Player * player_new(int id); void player_free(Player *); VEC2D player_get_position(Player *);`, etc. You only define the internals then in the player.c file. Player new might even return a pointer from a static array for extra speed over malloc(). Or why not even typedef int Player;, for even better encapsulation.
So call me a heretic. OOP isn't a silver bullet. Before the hype, the name of the game was simply "modularity", and that is by far the more important concept.
One more note, which will thoroughly confuse you, is that there are tons of design patterns--that is, ways to frame data. And they all have their advantages and disadvantages.
But don't worry, it'll sink in over time.
/*
They call it C++ for a reason. Hiding of the this pointer is very nice and tidy, except for the fact that no one has a clear standard for symbol mangling in C++, which is an oversight of the C++ committee I think, because it sacrifices portability.
Another major feature of C (plus plus) is inheritance and polymorphism, which is obnoxious to implement in C. I hate writing my own vtable and having to swap out members based on configuration. And why oh why would I ever want to write my own vector implementation because I have to - it's C. It was a breeze to implement N-Dimensional Arrays of any type using templates and recursion.
I feel like I am tying my ankle to my butt when I code in C. Having to hop around on one foot when I have two perfectly good feet.
It is sad though, the state of MinGW. The C++ standard is still not implemented fully. I can't even use std::thread without using MinGW-w64.
I can see why low level library implementors would use C though, for its portability. But for higher level tasks I think C++ is the way to go, and since it interfaces with C just fine you don't have to change hardly any of your code to do it. If Java wasn't entirely contained in headers and slowed down by interpreting code and if it had a better gui and layout library, I would go even further and say to use Java instead even.
To get back on topic, what have you tried so far, and what are your results? How is your understanding of classes / structs? Do you know array syntax yet and see how it can help you?
Basic C array handling with an example button class :
//*/
And since I provided an example in C I think Beoran should have to provide an example in C++. Feel my pain bro.
If you have any questions on what is posted here, ask away. Do your best to understand the code presented here though.
Attached 7zip file of source and exe , 7-zip download site
{"name":"608893","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/6\/d6e233e7ca59d3aeaa400a2ca71a9aaa.png","w":1280,"h":800,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/6\/d6e233e7ca59d3aeaa400a2ca71a9aaa"}
Note: rename "libzlib.dll" to "libz.dll" in Edgar's attached example to run it.
Nice example of some fine C programming.
The only thing that bugs me about it is if any of the initialization fails, and the program exits, you'll have a memory leak due to cbuttonarray not being freed. But I understand this is just an example.
I'm too lazy to do proper deallocation in C these days. I figure if something went wrong enough to quit the program you have bigger problems than a memory leak that will be cleaned up by the OS anyway. </lame_excuse>
For safety it should have been this :
But it looks like 'free' accepts a NULL pointer so it doesn't matter.
The only thing that bugs me about it is if any of the initialization fails, and the program exits, you'll have a memory leak due to cbuttonarray not being freed.
I thought we went over this a few days ago? Any modern OS will release memory when the program exits. It's when you're continually leaking memory, for instance in an image viewer, where the user may click on scores of pictures before exiting the program, that the leak becomes a problem.
I thought we went over this a few days ago? Any modern OS will release memory when the program exits.
You're probably right. We didn't go over this, maybe someone else. But I like to handle that anyhow, to be safe.
"Never assume, it makes an ass out of you and me."
Edit: Just read the thread on "Memory Leak?", very interesting. I'll keep freeing it anyhow, but..
Minor fix to draw button text properly centered :
void draw_cbutton(CBUTTON* cbutton , ALLEGRO_COLOR c , int i , ALLEGRO_FONT* font) { al_draw_filled_rectangle(cbutton->x , cbutton->y , cbutton->x + cbutton->w , cbutton->y + cbutton->h , c); al_draw_rectangle(cbutton->x , cbutton->y , cbutton->x + cbutton->w , cbutton->y + cbutton->h , al_map_rgb(255,255,255) , 2.0); //void al_draw_textf(const ALLEGRO_FONT *font, ALLEGRO_COLOR color, // float x, float y, int flags, // const char *format, ...)al_draw_textf(font , al_map_rgb(255,255,255) , cbutton->x + cbutton->w/2.0f , cbutton->y + cbutton->h/2.0f - al_get_font_line_height(font)/2.0f , ALLEGRO_ALIGN_CENTRE , "Button #%i" , i);}
I thought we went over this a few days ago?
I thought you explained that you had an error that took up memory even after your program exited, and only running a possibly obscure Linux command would the kernel process and free it.
I thought you explained that you had an error that took up memory even after your program exited, and only running a possibly obscure Linux command would the kernel process and free it.
Shared memory. Shared memory. Repeat until you grok.
Edgar, we'll probably have to agree to disagree there.
Before I also used C++ a lot but I got burned repeatedly and suffered greatly because of C++ until finally I realized plain C + a nice scripting language like mruby (or lua or python if you prefer that) is a much better solution for me. Sorry for not taking you up on your offer for the buttons, I already implemented the Allegro haptics driver on windows for directx in C++ so I already did my part. Ugh!
I do agree that C needs more libraries for data structures and algorithms but I'm slowly working on solving that problem too. I'm not in favor of templates since they exchange speed for space (how much code is generated behind your back?), but with a good x-macro you can get the same effects. And I love explicit vtables like the ones Allegro uses, they give me full control, and allowed me to write a very simple wrapper driver for the windows joystick around the directx and xinput driver.
Edgar, I think you're sort of missing the point with dependency injection. A large part of the advantage to true OO is polymorphism. That's probably the most useful feature of it. Inheritance can be useful, but is often abused.
When you have truly modular, decoupled code you end up with things like (contrived example):
That is all handled through a defined set of interfaces which can be implemented however you want, as long as the API semantics defined are obeyed properly. This makes code extremely easy to test. Instead of charging your credit card every time you run the test suite, the necessary layers just pretend to contact the credit card company and really just flag enough state to fake it.
You can take advantage of that for various things like testing, or for distributing the load of a complex project among many people, and allowing people to work on their isolated segments of the application while the unimplemented stuff just pretends to exist. If something isn't done yet create an acceptable mock for it and use that. In that case, you'd also want some kind of factory object to generate these instances.
pp.process( factory.create_db(), factory.create_payment_service_proxy(), order);
During development or testing the factory might generate a mock object for the database manager or payment service proxy, but in production it will create the real thing, initialized with real data and running real code. This makes it trivial to completely change the behavior of the program from a single place instead of having to go and change thousands of complex, unpredictably written statements (the latter of which is far more error prone and depending on how you do it, far more time consuming).
For safety it should have been this:
*snip*
But it looks like 'free' accepts a NULL pointer so it doesn't matter.
In other words, no, it shouldn't have been that at all.
You should split your main function into multiple functions, and then name each of those function with a proper name that accurately describes what it does. Here's an example of splitting using your existing code (probably not compilable):
How would this make your life easier?
1. It makes it easier to code. If you want to load a new resource, just find the function load_resources and add the necessary code, instead of scrolling around finding the correct line to add the code inside a big main function. It may seem tolerable now, but when your code reaches thousands of lines, it's really painful. Most modern IDEs or even text editors provide the functionality to jump to a function via a drop down.
2. It makes it easier to read your code. The function's name act as a summary of what the code contained inside it does (if you keep a habit of doing so.) This is very important because you will definitely forget your code in a few weeks/months, and when that happens, it's easier to understand what the hell is going on.
3. It makes your code reusable. If you have a functionality that takes a few lines of code to achieve, and you find yourself copy-pasting that code over and over again, then it's best to put it inside a function.
There are more to it, but I'm supposed to be working right now!
Shared memory. Shared memory. Repeat until you grok.
If its shared, then its not really freed then is it
How is your understanding of classes / structs?
My understanding of structs is not good.
I haven't even tried working with structs yet.
But my understand of classes, I would say is rather okay.
Basically my understanding is, when you create a class, you can create an object either globally (Bad) or inside a function (Better), like so:
Button bo;
And this will create an instance of the Button class called bo, which we can now use to get the result of a function inside the bo instance class.
bo.getName();
This will get the output of the getName() function inside the bo instance, not the Button class right?
I will try to study all of you peoples code, that you posted here, and I will play with it until I get a basic understand on how to create my buttons in a better not-hardcoded way
// The global variables
ALLEGRO_DISPLAY *display;
ALLEGRO_EVENT_QUEUE *event_queue;
ALLEGRO_TIMER *timer;
ALLEGRO_BITMAP *abg;
ALLEGRO_BITMAP *bg;
ALLEGRO_BITMAP *menub;
ALLEGRO_BITMAP *login;
ALLEGRO_BITMAP *menub2;
ALLEGRO_BITMAP *creation;
ALLEGRO_BITMAP *readfirst;
ALLEGRO_SAMPLE *lsm;
I thought it was bad to use global variables? :S
My understanding of structs is not good.
I haven't even tried working with structs yet.
But my understand of classes, I would say is rather okay.
AFAIK, in C++, the difference between a struct and a class is that struct members are public by default, while class members are private.
So if you understand class, then you understand struct. They are pretty much the same.
I thought it was bad to use global variables? :S
They're generally bad, but it wouldn't hurt to use them in a small, single-file, throwaway projects. Plus, I was just giving an example, so I didn't bother.
In other words, try not to use them.
It's okay I have modified the code you showed me, and I have put it all in classes.
Global variables I changed to private, seems like I am getting to know this better
See all those instances of 'hover*'? You can replace them with a single instance member variable in your Button class called hover, and use one for clicked as well. Then when you want to see if a certain button has been clicked you check it's HasHover() or IsClicked() method.
Edit
You actually don't even need those variables. Move the logic into HasHover(int msx , int msy) and IsClicked(int msx , int msy).
I thought it was bad to use global variables? :S
I have no problem using them. In his example is a good example of global variables. This way he can call shutdown() from anywhere in his program and it will free up the allocated memory.
It's the only reason I will use them, for this like this where you have a variable allocated like the display which you will need freed up later and possibly used from several locations.
There are ways to avoid this, but I don't see how creating more confusing code JUST to avoid globals is a good thing, so you need to take what people say with a grain of salt.
My understanding of structs is not good.
I haven't even tried working with structs yet.
Structs originated with C. They're basically like classes, only without functions. At least in C. C++ treats structs as classes with it's member's public by default. Here's a nice explanation of them on this website...
http://www.cplusplus.com/doc/tutorial/structures/
IIRC, the problems with global variables are:
They have global names, so it can be hard to come up with meaningful names that don't clash.
They aren't optimized as well by the compiler, since the compiler may decide to keep a local variable in a register only.
They're not allocated on the stack, so even if a local variable isn't held entirely in a register it's pretty much guaranteed to be cached since the stack was accessed just for the function call.
They aren't optimized as well by the compiler, since the compiler may decide to keep a local variable in a register only.
If optimization is your problem, your actual problem is everything you've done in the entire project. (Read: Bad algorithms and bad design.) Since optimization generally gets you 20-30% speed up at best, whereas algorithm selection controls orders of a magnitude speed up and slow down of your program.
So I would never really consider that an issue for using or not using globals. Globals are like GOTO statements. If you design everything properly, you probably (but not always!) won't need them. But they can be very handy if used sparingly for rapid development.
But I'm just saying that for general readers--I know you know all this.
Using localized state far outweighs any perceived "benefits" of global state. This little snippet demonstrates that you can easily pass around an "application scope" chunk of memory to the places that need it (only) with little extra code. The main advantages are that you can easily see which state affects a particular function in the program (because we're smart people that refuse to reach into the global namespace), and you can easily make changes to it without affecting the rest of the program and without having to do extra work [carefully] to undo the tampering that you've done, without worrying that unrelated parts of the program could affect us.
This also allows you to easily test the code. You can write little tests that set up an APP_STATE exactly how its needed and see how little chunks of your program respond to it without having to invoke a long chain of the main program. Your tests can even run within a single process without having to tear down and start back up because you've remained smart and kept state local guaranteeing (with the exception of memory access bugs) that one test will not tamper with another one.
If you have never done it then give it a try. Honestly, it is so much nicer when the needs of a function are explicitly defined. You gain so much for so little. It will also make you a better programmer guaranteed because it will force you to think in situations where global state would let you be lazy. This will help you to discover mistakes in your design earlier and fix them before they become the cause of a week long debugging session and another week of application-wide refactoring.
Just. Don't. Use. It.
Append:
I'd rather see goto than global state. I'm convinced the latter is a bigger evil than the former.
If optimization is your problem, your actual problem is everything you've done in the entire project. (Read: Bad algorithms and bad design.)
I didn't say optimization was a problem, I said you get more optimization "for free", i.e. no tricks or hard to read code, by using locals. OTOH, for a little toy program the globals would likely fit in the cache too.