Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Code review

This thread is locked; no one can reply to it. rss feed Print
 1   2 
Code review
Adam Kazimierczak
Member #11,613
January 2010

Hello!
I am newbie C++ programmer.
Last time I wanted to learn object-oriented design,
so I tried to write easy C++ application with classes.
I wrote a Langton's Ant cellular automaton simulator,
you can find my code here:
http://www.allegro.cc/depot/LILACALILACAisLangonsAntCellular

If anyone have some free time and want to help me,
you can download sources and review my program.
Whole code is about 300 lines, with about 100 lines of comments
and some lines in .h file.
All comments will be very usefull and helpful.

Thanks for help,
Adam

OnlineCop
Member #7,919
October 2006
avatar

I'm unable to even get it to compile on Mac.

Compiling with debugging, I get a crash within main.cpp, on the call to "langton.start();", it giving me the error:

(gdb) r
Starting program: /Users/onlinecop/programming/temp/allegro/602869/LILACA/lilaca 
Reading symbols for shared libraries ...............................................................................+ done
Reading symbols for shared libraries . done

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0xb0001a70
[Switching to process 3049 thread 0x3213]
0x00003ba7 in _mangled_main () at main.cpp:42
42              langton.start();
(gdb) kill
Kill the program being debugged? (y or n) y
Current language:  auto; currently c++
(gdb) q

(My line numberings are different because of some stuff I played around with to get it to work.)

Using Allegro 4.2.3.

Dario ff
Member #10,065
August 2008
avatar

Same deal here :-/

{"name":"600310","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/8\/685a603f1660624021e023b6dc4991df.jpg","w":887,"h":539,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/8\/685a603f1660624021e023b6dc4991df"}600310

I suppose you were lucky enough to don't have a memory crash.

EDIT: Can anybody explain me who invented the "this->" crap? I've seen this repeated a lot, and I don't find it any different than calling it without it.

EDIT2:

This is the culprit:

#SelectExpand
40void Field::reset() {
41 for(unsigned int i=1; i<=800; i++) {
42 for(unsigned int z=1; z<=800; z++) {
43 this->set_fill(false, i, z); 44 } 45 } 46 this->filled_cells=0; 47}

it should be:

#SelectExpand
40void Field::reset() { 41 for(unsigned int i=0; i<800; i++) { 42 for(unsigned int z=0; z<800; z++) { 43 this->set_fill(false, i, z); 44 } 45 } 46 this->filled_cells=0; 47}

EDIT3:
I can see this error repeated everywhere(counting from 1 instead of 0) :-/

Remember, a C programmer always starts from 0:
{"name":"castigo.gif","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/9\/a98f488ebef42572340874c0a5ba611f.gif","w":600,"h":197,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/9\/a98f488ebef42572340874c0a5ba611f"}castigo.gif

;D

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

bamccaig
Member #7,536
July 2006
avatar

dario ff said:

Can anybody explain me who invented the "this->" crap? I've seen this repeated a lot, and I don't find it any different than calling it without it.

It's intended to fully qualify the variable, which makes the code more clear and also avoids subtle bugs like C::aab versus ::aaa (AKA, a global variable aaa). It's easy to accidentally type the latter and the compiler won't complain if it's compatible with the context it's used in. If you want to be really sure, you can always go with this->C::aab. :P

Dario ff
Member #10,065
August 2008
avatar

bamccaig said:

makes the code more clear

To me it looks really messy. To each it's own I guess ::)

bamccaig said:

avoids subtle bugs like C::aab versus ::aaa (AKA, a global variable aaa).

You edited fast ;D I saw an x there. That's more reasonable.

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

Audric
Member #907
January 2001

The only arguments for "this->" that I can agree with are:
- When writing an example, showing that we're referring to a class member.
- When using an IDE with autocompletion, getting the IDE to provide you just the class members.

anonymous
Member #8025
November 2006

This is a kind of situation where this-> is very convenient (A<T>::foo is non-dependent, hence the compiler needs to be explicitly told that it comes from the template parent) although there are other, more verbose ways:

template <class T>
struct A
{
    void foo() {}
};

template <class T>
struct B: public A<T>
{
    void bar()
    {
this->foo();
} };

Adam Kazimierczak
Member #11,613
January 2010

I thank you all for so fast replies!

I know that counting is starting from 0 (I programmed before in asm, php, some bash and perl),
but I had segfaults when it was 0 (I dont know why),
now is ok when I hanged i<=800 to i<800.
But thanks for advice.

I tried to compile my code under windows (on virtual machine),
after some time with troubles with compilation (windows is very strange system)
I run dbg and changed some of my code:

void Field::reset() {
    for(unsigned int i=0; i<800; i++) { // i=1 and i<800 (from <=800)
        for(unsigned int z=0; z<800; z++) { // same here
            this->set_fill(false, i, z);
        }
    }
    this->filled_cells=0;
}

same for Field::draw_dots.

I also received segfault here (Info::draw_info):

textprintf_ex(buffer, font, 10, screen->h-20, makecol(255,0,0),-1, "To leave press [ESC]");

So I changed "screen->h-20" to "780" (I am not planning changing resolution in future).

Last error is appearing here (at System::start()):

blit(this->buffer, screen, 0, 0, 0, 0, 800, 800); // Update screen

Quote:

Program received signal SIGSEGV, Segmentation fault.
0x679cc350 in blit ()

I have no ideas why there is segfault.
Any ideas?

EDIT:
about "this->",
so I should use it always, but only when needed?

Dario ff
Member #10,065
August 2008
avatar

I've made some modifications. One of the most important ones is the one marked with ***. Here.

I'll explain:

  • If you're going to use a constant value such as 800, you better define a Macro or a constant variable somewhere. I added in langton.h:

#SelectExpand
1const int FIELD_SIZE_W=800; 2const int FIELD_SIZE_H=800;

And anything related to the field used this constant. This way, in case you need to change the size of the field, you won't need to code everything again.

  • step in the Info class wasn't initialized with a suitable value. I added an init() method for the Info class, and called it as soon as the application started. You were getting WEIRD values for current step.


  • I added this in the System::init()

srand(time(0)); // ***

You weren't giving to rand any value, so you always had the same results for your rand calls. I gave it a value related to the system's clock. As you probably know, computers can't handle random, it's just math that appears to give random values, so you've got to give it a value. It's funny that someone based on this in a casino and won the electronic lottery by predicting the numbers with a computer program.

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

Adam Kazimierczak
Member #11,613
January 2010

Thanks for your corrections Dario_ff,
but I still have segfault in same place:

Program received signal SIGSEGV, Segmentation fault.
0x0040faa0 in blit ()

By the way, it's very interesting method you used in conditions in loops:

for(unsigned int i=0; i!=FIELD_SIZE_W; i++)

BAF
Member #2,981
December 2002
avatar

I know that counting is starting from 0 (I programmed before in asm, php, some bash and perl),
but I had segfaults when it was 0 (I dont know why),
now is ok when I hanged i<=800 to i<800.
But thanks for advice.

So write crappy sloppy code that for unknown reasons solves the segfault you shouldn't have been getting? That's just about the polar opposite of writing good code.

for(unsigned int i=0; i!=FIELD_SIZE_W; i++)

Should be <, not !=. != will happily let it go beyond FIELD_SIZE_W if somehow i gets incremented past the limit, say by an extra i++ in your loop body or something.

OnlineCop
Member #7,919
October 2006
avatar

I'd imagine that within the processing pipeline, the advantage of using != is that it simply checks for equality then negates that result; much faster than the <, which subtracts the one from the other and checks whether that sets the overflow/underflow bit, the values are still unequal, etc.

The disadvantage is that if you happen to 'hop' over the value you were checking for, you've got either 2^32 or 2^64 more loop iterations to go through before it overflows back to 0 and starts again. Of course by then, you've either segfaulted your application or unplugged your computer for the length of time it's taken...
;)

Dario ff
Member #10,065
August 2008
avatar

BAF said:

Should be <, not !=. != will happily let it go beyond FIELD_SIZE_W if somehow ii gets incremented past the limit, say by an extra i++ in your loop body or something.

NO, it should be !=. I won't hog the CPU for "safety" when doing loops of 800X800 :P And if you use an extra i++? Then check if the value "hopped" above the limit, and break.

Thanks to Online cop for the tech explanation. ;)

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

Thomas Fjellstrom
Member #476
June 2000
avatar

dario ff said:

NO, it should be !=.

No. It shouldn't. In the off chance the value goes over the right hand side, it will keep looping till it wraps around. it SHOULD be <. So it will always break when i is equal to or greater. Instead of only when it equals the right hand side.

Quote:

I won't hog the CPU for "safety" when doing loops of 800X800 :P

That makes no sense.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Dario ff
Member #10,065
August 2008
avatar

Yes it does. At least to me... As OnlineCop said, checking for equality is much faster than <.

What off chance are you talking about?

You know what? Nevermind, I've never had problems with !=.

EDIT: Seriously, why are we talking about off chance? I already said that if you put an extra i++ you check the value and break :P

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

Audric
Member #907
January 2001

I'm dubious about the statement on '<' being "much slower than '!=' in the first place (anybody got real-world facts on assembler instructions JNE and JL ?)

And in any case, I find it a bad idea(tm) to teach a self-called "newbie C++ programmer" to alter his way of coding an expression. Not before he has completely mastered structured programming; and counting "from 0 included to MAX-1 included" is really part of the basics.

Dario ff
Member #10,065
August 2008
avatar

Ok ok, use < instead of !=. IDK why I learned this way.

He says he didn't do the counting in the right way because he got segfault. Obviously, he screwed up somewhere else, and he was lucky that changing the value didn't crash at start. I'm feeling generous, so I'll read the whole code to see what could be causing the segfault :P

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

Tobias Dammers
Member #2,604
August 2002
avatar

dario ff said:

As OnlineCop said, checking for equality is much faster than <.

Not in any meaningful way. Maybe one or two clock ticks per iteration; on a 2GHz dual-core CPU, that's half a nanosecond. Less than the time a photon travels from the screen to your eye.

First of all, the loop overhead is negligible in 99% of all the loops you'll ever write. One or two extra clock ticks on a loop that takes thousands of clock ticks per iteration is literally nothing. If such a loop turns out to burn too much CPU time, see if you can get away with less iterations, with less calls to the loop itself, or with a better algorithm. Only if you are already using the best possible algorithm as sparsely as possible, and you are still having performance problems, consider optimizing code. But before doing so, give the compiler a fair chance of doing it for you (-O2), and when done, be sure to profile so you can be sure the code is really faster. You'd be surprised.

Quote:

EDIT: Seriously, why are we talking about off chance? I already said that if you put an extra i++ you check the value and break

Consider the following code:

for (int i = 0; i != 10; ++i)
  foobar(i);

Is this safe or not? You'd be tempted to say "yes", but what if foobar is defined like so:

void foobar(int& i) {
  ++i;
}

It is not an off chance, it is a very realistic possible source of bugs that are extremely hard to find.
The only situation where I'd use != over < to iterate is with STL iterators: These often don't have a less-than comparison operator, and incrementing some_container::end() does nothing, so the iterator cannot possibly overrun (it can invalidate though, but that is a different question).

I know that counting is starting from 0 (I programmed before in asm, php, some bash and perl),
but I had segfaults when it was 0 (I dont know why),
now is ok when I hanged i<=800 to i<800.
But thanks for advice.

This approach is known as shotgun debugging and generally considered bad practice.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

GullRaDriel
Member #3,861
September 2003
avatar

What if it is void foobar(int& i) ? Then you should have used C instead of C++ !

And you would have got to use a void foobar( int *i) and a call on foobar( &i ).

Which is way more talking than the C++ version who hide the fact that the function can possibly modify the values passed as its parameters.

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Dario ff
Member #10,065
August 2008
avatar

Ok, I haven't looked at the whole code, but I made some other changes.

And because of the massive bitching around here, I replaced != with < (but I think that just for pissing all of you I'll put !(i>=FIELD_SIZE_W) ;D

I think the error you have in your VM is because it doesn't support your res. Remember the desktop res must be >= to the window's res. Remember you have a square, 800x800, and it's possible your VM res is lower than that.

TranslatorHack 2010, a human translation chain in a.cc.
My games: [GiftCraft] - [Blocky Rhythm[SH2011]] - [Elven Revolution] - [Dune Smasher!]

anonymous
Member #8025
November 2006

There seems to be lots of non-sense concerning < vs != :)

A C++ programmer would be quite used to !=, since more often than not he uses iterators, which more often than not are not random access iterators and don't support less-than comparison in the first place.

Secondly, if the purpose of the loop is to go from 0 to N with no skipping and things, having it get stuck in an infinite or a very long loop (since the loop counter is unintentionally modified somewhere) is a good thing, since it helps you detect that something is wrong. If the loop just appeared to run normally, it might be harder to find out that the program is not running properly.

There seems to be a belief that code that makes efforts to cover up bugs is "safe" compared to code that rings a warning bell as soon as something is wrong.

Adam Kazimierczak
Member #11,613
January 2010

Audric,
I am newbie programmer in C++, but before I coded for many years
( I remember time when I was writing BASIC code at C64).
I used 1, because 0 caused segfaults on VM (on my GNU/Linux was work).

Dario_ff,
it's not VM fault.
I have VMWare with vmtools installed.
Default resolution is 1280x768 and... WAIT.
I completly forgot about it!
You have right, it's VM fault.
Thank you very much, now my program is working propertly!

Thank you all for helping me with segfaults and others!

Can we make a little summary?
I mean what faults I made in obiect-oriented model (with classes and others),
"this->" problem and others like that.
Here is final, working code:
http://www.allegro.cc/files/depot/2285/lilaca-1.0.tar.gz

CursedTyrant
Member #7,080
April 2006
avatar

"this->" problem and others like that.

It's not a problem, it's just unnecessary.

---------
Signature.
----
[My Website] | [My YouTube Channel]

OnlineCop
Member #7,919
October 2006
avatar

#SelectExpand
42int i; 43for (i = 0; i != rand(); i++) 44{ 45 so_nyeah(); 46}

EDIT:

I had to change a few other things on my system so it'd work. Notably, you're still creating your screen dimensions at (800, 800) and setting your ant's initial position to (400, 400). But you've defined your array to be 800x600 through use of FIELD_SIZE_W and FIELD_SIZE_H.

So change within your langton.cpp the lines:

    set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 800, 0, 0);

to

    set_gfx_mode(GFX_AUTODETECT_WINDOWED, FIELD_SIZE_W, FIELD_SIZE_H, 0, 0);

and both the ant's position and the this->buffer dimensions to the new screen dimensions:

    this->buffer = create_bitmap(800, 800);
    ...
    this->ant.set_position(400, 400); // Center of view

to

    this->buffer = create_bitmap(SCREEN_W, SCREEN_H);
    ...
    this->ant.set_position(SCREEN_W / 2, SCREEN_H / 2); // Center of view

Then if you change your dimensions again, everything will be fine.

Audric
Member #907
January 2001

Adam: Ah ok, sorry I didn't watch closely.

anonymous said:

There seems to be a belief that code that makes efforts to cover up bugs is "safe" compared to code that rings a warning bell as soon as something is wrong.

More generally, there are two opposite extremes that are bad:
- The paper-thin programs that never care if what they do succeeds or not (and rely on it working); these programs crash if anybody looks at them sternly.
- The "On error resume next", bulldozer programs. They recover from everything without even knowing what hit them.
In both case, it's because the programmer hasn't decided what to do if an unusual event happened.

I can cite 2 projects where user safety was more important than programmer convenience:
- a graphic editing program. Catches signals, re-allocates memory defensively, makes pre-emptive backups; anything I can do to avoid data loss from the graphix man.
- a persistent MORPG server (it was multiplayer but not massive :) ) You check "world" and pointers consistency everywhere, you stop executing a command or spell if it bugs out, and continue playing : Don't halt the server. It's harder for the coder to troubleshoot bugs, but players wants the world online 24/24, even if one item, command or spell happens to have no effect.

 1   2 


Go to: