Don't rewrite your old code
Mark Oates

nice little article here

Thomas Fjellstrom

Hah. In some cases you have to rewrite. Ironically, like the Netscape 4. From what I gather it was SO horrible, that they couldn't continue with it. I know for a fact that the later 4.x versions were horribly buggy, and lacking in features.

Matthew Leverton

I don't agree with his blanket statement. (Yes, I read the article.) I'm sure it's true under some circumstances, but in my experience if you come across code written by incompetent fools, you are better off burning it and starting over.

For instance, at a previous job in a previous life, a co-worker had been working on a web project for three months. The project manager came to me and asked me to "take over" since the deadline was rapidly approaching. After taking one look at the code, my eyes started to bleed. That weekend, I re-wrote the entire thing from scratch. Came in on that Monday, and handed it him. I couldn't care less that it meant they just wasted three months of salary on her.

Now I know that Joel is talking about re-writing code that already "works," so my example may not be at all what he is talking about. But he considered Netscape 4 as something that "worked," which is a very bad example. Netscape 4 was very screwed up; IE 4 was blowing it away. He obviously never had to look at it from a developer's perspective.

Thomas Fjellstrom
Matthew said:

He obviously never had to look at it from a developer's perspective.

I don't think he looked at it from a USER's perspective.

Matthew Leverton
Quote:

I don't think he looked at it from a USER's perspective.

To be clear, I mean a 3rd party web-developer as opposed to a Netscape developer.

Michael Faerber

Old.

[EDIT]
Although I agree with the article.

Paul whoknows
Quote:

It's important to remember that when you start from scratch there is absolutely no reason to believe that you are going to do a better job than you did the first time.

My personal experience:
One thing to avoid the "start from scratch all over again" is to re-write and re-design your project from time to time, to make it more readable.
In my current project I re-designed a few classes that already worked perfect! but I was not happy with them, because they looked a bit messy, I spent time in the re-design, and I am sure that players will never notice the difference, but as a developer I feel better, because now I have a more legible code.
I allways try to achieve readability because, as the articles said:

Quote:

It's harder to read code than to write it

Dustin Dettmer

Ya I'm not sure about that point. I doubt source code from Netscape 4.0 would stand a chance against all the new standards for CSS and javascript. Maybe some code could be preserved, but the majority could not.

In the feedback link they talk about redoing the FTP code. Heh, now that makes no sense at all.

Bruce Perry

That Joel guy would not get a job at the company I work for. :-X

Quote:

There's a subtle reason that programmers always want to throw away the code and start over. The reason is that they think the old code is a mess. And here is the interesting observation: they are probably wrong. The reason that they think the old code is a mess is because of a cardinal, fundamental law of programming:
It's harder to read code than to write it.

Let me reword that.

Quote:

There's a subtle reason that programmers always want to throw away the code and start over. The reason is that they think the old code is a mess. And here is the interesting observation: they are probably right. The reason that the old code is a mess is because they don't know a cardinal, fundamental law of programming:
If you code like a muppet, neither you nor anyone else will even bother trying to read your code. (It might help to get a clue before rewriting it.)

We have some golden library design rules at work. They work amazingly well. We can understand our code six months later (shock!), and we can all modify each other's code with confidence and success. It's great! :D

[EDIT]
Actually, he changes his mind halfway through. He starts off by saying that old code isn't messy, but then he changes his mind and concedes that actually it is messy and does need careful refactoring (which is often true). He might get a job then. ;D

Thomas Fjellstrom
Quote:

To be clear, I mean a 3rd party web-developer as opposed to a Netscape developer.

Ah, ok. But to be clear, I mean from a plain old user's perspective.

Dennis

Slightly off-topic: Among artists it is common practice to redo some of their old works to learn and to see how they have improved over time. Maybe that's the same reason to rewrite old code, aside from the functional reason of course (code that doesn't work just needs to be fixed/rewritten, whereas a crappy piece of art can still stay as it is, as it doesn't have a well defined function like code has).

ReyBrujo

That is called extreme programming: you program thinking in today and dismissing tomorrow. So, instead of thinking "I may need a new argument for this function" you say "I only need these arguments for this function". It is good for evolutionary design, the one where you need to create a prototype, show it to clients, get suggestions, and then update the prototype with the suggestions. We used it at work, but found it to be costly (especially when you have a limited number of programmers).

The truth is, when you are dealing with a 9 year old product (like the one I work with), you usually find several old paradigms that are outdated, and need to write a lot of code again. Personally, in four years I threw many components away, trimmed others, and fully recreated the contraindication module, the drug database, the graphical interface, allergies and, after three weeks, the settings (so that you can query for settings or show the graphical interface to change settings anywhere in the program, for maximum customization). None of these changes would have been possible with the old code (to give some examples, in Visual Basic nobody used implementations until three years ago, there were copies of the same function everywhere, and there were even rem comments... and in the C++ part, a component had created the full COM interface manually, hardcoding the GUIDs, another had an abusive hierarchy of 15 levels, none of which has siblings--think in a hierarchy that looks like a vertical line without branches--, and another was a horizontal one, where leaves did not inherit from each other but instead always repeated code from other classes).

Gamasutra has a feature about it, Embracing Fun: Why Extreme Programming is Great for Game Development.

Simon Parzer

"Don't rewrite your old code" is a great title, actually. It doesn't say "Don't rewrite somebody else's old code".

Sometimes the title is ten times better than the article itself :P

Bob

Joel makes a good point, from a business perspective. If you've got a product that "works", reverting back to something that doesn't makes no sense at all.

That doesn't mean that a certain subset of the code can't be rewritten. In fact, it's probably a good idea to pull out a chunk of code, do some spring cleaning (and by which, I mean rewrite it), and refit it back into the codebase.

When you're dealing with 0.25+ MLOC codebases, rewriting and redebugging them in their entirety is often not a viable option.

Quote:

We have some golden library design rules at work. They work amazingly well.

How well do they work on a long-term basis? Over a 3-5 year basis, yes it's possible to come up with rules and techniques to keep things manageable. When you have a 8+ year old codebase, a lot of things tend to change over time to make the existing code inadequate.

The metric I use is a shelf life of good quality code is 5 years. Call it Bob's Law if you will ;) Basically, you will need to rewrite your code every 5 years. Plan for it.

Code does rot. Not because the code itself functions less over time, or because defects magically insert themselves. It's simply because the requirements change, and keep changing even after the product has shipped. These requirements may not even be under your control, or the control of your company. For example, the OS changes, CPUs get faster, you now need a port to some other platform, etc. The existing code, being static by definition, does not keep up with these changes.

When you do need to make changes to the codebase, you also need to look at things from a business perspective. What's the !/$ of the change? Do the small incremental changes you need to make warrant a complete rewrite to make them a better fit for the architecture?

Of course, you also need to weigh in some metric based on the "mass" of the code. Is the code in such as shape that it will collapse onto itself and turn into a blackhole, sucking all development time into nothingness?

I don't know what the state of Netscape 4 was at the time. In retrospect, it was definitely a bad idea to rewrite it from scratch. But the neat thing about hindsight is that it's always 20/20. It cost Netscape 2 years of time where they had no new product on the market. No new product == no income. Loss of market share and mindshare. That's a really bad position to be in, business wise.

GullRaDriel

I am shared.

Don't we always say that reinventing the wheel is bad ?

Of course, if the code is a total mess, then rewrite ! But if it is good code, nicely organized and structured, where is the need huh ?

Thomas Fjellstrom
Quote:

I don't know what the state of Netscape 4 was at the time. In retrospect, it was definitely a bad idea to rewrite it from scratch. But the neat thing about hindsight is that it's always 20/20. It cost Netscape 2 years of time where they had no new product on the market. No new product == no income. Loss of market share and mindshare. That's a really bad position to be in, business wise.

Netscape didn't make any proffit from The browser at that point anyway, Microsoft forced them to give it away. All it cost was the manpower to write it. And they did get some help from the community (albeit not that much).

Netscape made a majority of their income from their server business even when they were selling the browser. And it shows in how they let the browser rot.

That said, excellent view Bob. I'm adding this to my threadbook.

Bruce Perry
Quote:

How well do they work on a long-term basis?

For the point I was making, they should work for ever. All our golden rules are ultimately designed to keep the code clear and easy to work with. Of course we have to rewrite or adjust code if requirements change (or if Sun break Java) - but we never have to rewrite code just because we can't understand it. Joel's point was that people have trouble reading other people's code or their own old code, and that just isn't true for us. :)

Now, Sun's standard library code, on the other hand ... :-X Jesus Christ. No wonder they break Java so often.

ReyBrujo

Something I have learned in five years as professional programmer: you never rewrite code to fix bugs, you rewrite it to add new features.

Bob
Quote:

Something I have learned in five years as professional programmer: you never rewrite code to fix bugs, you rewrite it to add new features.

I do exactly the opposite! But then again, the code I work with has a low average quality.

Trezker

If the wheel doesn't fit the cart, you need a new wheel.

Johan Halmén
Trezker said:

If the wheel doesn't fit the cart, you need a new wheel.

Then invent it!

Indeterminatus
Quote:

If the wheel doesn't fit the cart, you need a new wheel.

Or a new cart. :P

HoHo

Or a new axel to piece together the two incompatible things :P

Matt Smith

..or an elephant to drag the cart along without its wheels. ..or an assassin to wipe out the client waiting for the cart. I'm sure these can be massaged into metaphors for certain industry practices :)

Trezker

When the cost of modifying the cart to fit the wheel is greater than the cost of getting a new wheel, you should get a new wheel.

Neil Black

Why use that old cart? When you can get... the Ultra-Cart 5000! Its fast, its sleek, its wheels are always compatible with the cart and the axle*. And what's more, it requires no elephant** and is assassin resistant***. Only two hundred seventy-six EASY payments of $99.99****! Supplies are limited, buy now.

-----------------------------------------------------------------------------------

*Wheel-axle-cart compatability not guaranteed.
**Elephant may be required in polar regions.
***Assassin defenses are limited to the delux edition.
****Must be paid in cash and all the payments are required within two days of purchase.

kronoman

I have a double moral.

I like to rewrite from scratch my own code, because I do it as a hobby, and is fun, and educational, and I usually get better results.

I don't like, and I don't think that is good (usually, there are cases were is good) to rewrite business code from working systems that are used to generate revenue. In those systems, I think that the KISS approach, and the "don't fix it until it is broken" really apply.

Trezker

That's what branching is for.
When something isn't broken but you would gain a lot by impoving it, you create a branch so you can work on it until it works without messing up the trunk.

Neil Black

my old code needs rewriting. And this is only a few weeks old ;)

it works, though.

1#include <fstream>
2 
3 
4int Dialouge(int TextC = 255, int TextX = 420){
5 clear_keybuf();
6 rect(buffer, 0, 410, 640, 480, makecol(255, 255, 255));
7 rect(buffer, 1, 411, 639, 479, makecol(192, 192, 192));
8 rect(buffer, 2, 412, 638, 478, makecol(128, 128, 128));
9 rect(buffer, 3, 413, 637, 477, makecol(96, 96, 96));
10 rect(buffer, 4, 414, 636, 476, makecol(64, 64, 64));
11 rect(buffer, 5, 415, 635, 475, makecol(32, 32, 32));
12 rectfill(buffer, 6, 416, 634, 474, makecol(0, 0, 0));
13 int x = 20, y = 420;
14 for (int i = 0; i < message.size(); i++){
15 textprintf_ex(buffer, font, x, y,makecol(255,255,255),-1,"%c",message<i>);
16 x += 8; // whatever your font may be
17 if(x >= 612){
18 x = 20;
19 y += 10;
20 }
21
22 draw_sprite(screen, buffer, 0, 0);
23
24 rest(25);
25 }
26}
27 
28int main(){
29 allegro_init();
30 install_keyboard();
31 set_color_depth(16);
32 set_gfx_mode( GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0);
33
34 buffer = create_bitmap(640, 480);
35 Col_Map = create_bitmap(640, 480);
36 Col_Block = create_bitmap(32, 32);
37 clear_to_color(Col_Block, makecol(255, 255, 255));
38 Col_Empty = create_bitmap(32, 32);
39 clear_to_color(Col_Empty, makecol(0, 0, 0));
40
41 Warrior = load_bitmap( "Characters/Warrior.bmp", NULL);
42 Grass = load_bitmap( "Tiles/Grass.bmp", NULL);
43 Floor = load_bitmap( "Tiles/Floor.bmp", NULL);
44 Water = load_bitmap( "Tiles/Water.bmp", NULL);
45 Rock = load_bitmap( "Tiles/Rock.bmp", NULL);
46 Tree = load_bitmap( "Tiles/Tree.bmp", NULL);
47 Door = load_bitmap( "Tiles/Door.bmp", NULL);
48 Stone_Wall = load_bitmap( "Tiles/Stone Wall.bmp", NULL);
49 Stone_Wall_Top = load_bitmap( "Tiles/Stone Wall Top.bmp", NULL);
50 Stone_Wall_Turret = load_bitmap( "Tiles/Stone Wall Turret.bmp", NULL);
51 Rock_Wall = load_bitmap( "Tiles/Rock Wall.bmp", NULL);
52 Wood_Wall = load_bitmap( "Tiles/Wood Wall.bmp", NULL);
53 Wood_Roof = load_bitmap( "Tiles/Wood Roof.bmp", NULL);
54 Cabinet = load_bitmap( "Tiles/Cabinet.bmp", NULL);
55 Bed_Top = load_bitmap( "Tiles/BedTop.bmp", NULL);
56 Bed_Bottom = load_bitmap( "Tiles/BedBottom.bmp", NULL);
57
58 Game_Speed = 50;
59
60 Load_Maps();
61
62 Plr.X = 256;
63 Plr.Y = 32;
64 Plr.Cell = 11;
65 Plr.Spd = 8;
66 Plr.Facing = 1;
67 Plr.Anim_Frame = 2;
68
69 while(!key[KEY_ESC]){
70 if(key[KEY_DOWN]){
71 if(Check_Collision(1) == 1){
72 Plr.Anim_Frame++;
73 if(Plr.Anim_Frame > 3) Plr.Anim_Frame = 1;
74 Plr.Facing = 1;
75 Plr.Y += Plr.Spd;
76 }
77 }else
78 if(key[KEY_UP]){
79 if(Check_Collision(2) == 1){
80 Plr.Anim_Frame++;
81 if(Plr.Anim_Frame > 6 || Plr.Anim_Frame < 4) Plr.Anim_Frame = 4;
82 Plr.Facing = 2;
83 Plr.Y -= Plr.Spd;
84 }
85 }else
86 if(key[KEY_RIGHT]){
87 if(Check_Collision(3) == 1){
88 Plr.Anim_Frame++;
89 if(Plr.Anim_Frame > 9 || Plr.Anim_Frame < 7) Plr.Anim_Frame = 7;
90 Plr.Facing = 3;
91 Plr.X += Plr.Spd;
92 }
93 }else
94 if(key[KEY_LEFT]){
95 if(Check_Collision(4) == 1){
96 Plr.Anim_Frame++;
97 if(Plr.Anim_Frame > 12 || Plr.Anim_Frame < 10) Plr.Anim_Frame = 10;
98 Plr.Facing = 4;
99 Plr.X -= Plr.Spd;
100 }
101 }
102 if(key[KEY_PLUS_PAD] && Game_Speed > 5) Game_Speed -= 5;
103 if(key[KEY_MINUS_PAD] && Game_Speed < 295) Game_Speed += 5;
104
105 if(key[KEY_SPACE]){
106 //Text1 = "Okay, so the dialouge works.";
107 //Text2 = "I hope...";
108 //Text3 = "Maybe this will confirm that hope,";
109 //Text4 = "but if it doesn't then I'll just have to keep working.";
110 //Text5 = "Wow, The formatting on this looks like crap.";
111 message = "Possumdude0: Okay, so this should display properly on screen, although there is no word-wrap, so I'll have to do it manually";
112 Dialouge();
113 }
114
115 Cell_Events();
116 Events();
117
118 Draw_Screen();
119
120 rest(Game_Speed);
121
122 }
123}
124END_OF_MAIN()

gnolam
Quote:

     rect(buffer, 0, 410, 640, 480, makecol(255, 255, 255));
     rect(buffer, 1, 411, 639, 479, makecol(192, 192, 192));
     rect(buffer, 2, 412, 638, 478, makecol(128, 128, 128));
     rect(buffer, 3, 413, 637, 477, makecol(96, 96, 96));
     rect(buffer, 4, 414, 636, 476, makecol(64, 64, 64));
     rect(buffer, 5, 415, 635, 475, makecol(32, 32, 32));
     rectfill(buffer, 6, 416, 634, 474, makecol(0, 0, 0));

Ever heard of for loops? :P
Then there's the clear_keybuf() (In a drawing function. When you're not even using readkey().), the non-returning main, the complete lack of return value checking... Gah!

And for the love of the Antichrist, stop using rest() timing...

Taiko Keiji

I would look into using a DATAFILE to store your images. it would make it easier to keep track of later.

Neil Black

I told you it needs re-writing.;D

Johan Halmén

At the present I'm very attempted to rewrite one application. It has a rather large allegro dialog struct and the indices are somehow wrong. Months ago I added something to the dialog and never got it ready. Now I need to make it ready and I have a hard time debugging it. It compiles and runs, but doesn't do the thing it should. And it is because indices are wrong. It's all about image manipulation and I have these radio buttons for whether creating a new file or overwriting the old. And some buttons for jpg quality. So when I wan't to make resized copies of all images in a folder, it overwrites all images with b/w, unresized, really screwed up versions of the images.

The main problem is only in my coding style. I haven't figured out how to write maintainable code what comes to allegro dialogs. It would be nice to have a way of using macros or something for the indices, so if you add one item in the middle of the dialog struct, you wouldn't have to change the indices all over the code. I bet masking or any other gui lib handles this better.

Bruce Perry

That code does not need rewriting!! :o

I read it quickly and understood it pretty easily. I don't know what all the functions you haven't included do, but there's nothing in there that looks particularly bad. Fixing the location of the clear_keybuf call is a question of moving the line from one place to another. The 'rest' timing thing is a bit harder to fix, but only because Allegro has such a horrible timing API (it doesn't even have a 'get current time' function) - and it's still only a question of writing more code, not throwing the old code away. (The problem is that the code running in between calls to 'rest' also takes time, thereby slowing the game down on slower computers, since gnolam didn't have the manners to tell you.) You could change it to use datafiles, but you don't have to; I don't see how it makes it easier, it just keeps everything nicely in one file and allows you to compress it. And there is no need to write that series of rects as a loop; the existing code is short and it's clear, and if you use a loop, you'll have to generate the series of colour values somehow - perhaps using an array. It'll also be slower. There's just no point.

Seriously, don't rewrite that code. There's nothing fundamentally wrong with it. There may be tweaks you can make, or you may have to rethink parts of it as you add to the project (but only if they don't work or start getting messy), but that's it.

Quote:

I haven't figured out how to write maintainable code what comes to allegro dialogs.

Sounds to me as if that's your only reason not to like the project. I also remember having a bit of trouble with that myself. I think I built the array up at run time, using a macro that fills all the fields in and then increments a counter - and then, when I needed to know an index, I just added a line that copied the value of the counter variable. That would be a bit of a 'rewrite', but you could still use a fair amount of creative copying, pasting, searching and replacing to get it done.

Johan Halmén

I use Julien Cugniere's dlg editor, which is actually very good. As good as the whole damn allegro gui system deserves. Worth its price, like Gimp. The editor does some nice things, it aligns all dialog entries nicely, you can muve each entry up or down in the struct (changing draw order), it draws even rects for custom procs. But when changing the order, you have to figure out the new indices and change your code manually. And that's the pain.

Neil Black

Did I mention that I have clear_keybuf() in almost every function? For some reason I couldn't get the dialouge to work any other way (the game would immediately read a second keypress and close the dialouge before the player could read it.)

Bruce Perry

Are you using key[] to check for your keys? All you're doing there is reading from an array, without changing its contents. The key[] array is the status of each key: whether it's held or not.

If you are using Allegro's GUI, then that uses readkey() to detect its key-presses. You should consider whether readkey() is better for your purpose too. You can use readkey() to get a key from the buffer - and have it removed from the buffer so nothing else can read it again - and if you don't want the function to block, you can call keypressed() first to check whether there is a key in there. See the docs for more information.

If you indeed find that key[] is more appropriate, then why don't you call clear_keybuf() in just the right place, just before you show the dialogue?

GullRaDriel

Possumdude0: It is because your code is a mess. ;D

Bruce Perry

Ta gueule.

Kitty Cat

clear_keybuf doesn't touch the key array. It just clears the keypressed/readkey queue. The only (valid) way to modify the key array is to press or release a key on your keyboard.

Goalie Ca
Quote:

Ta gueule.

Tu peux la fermer? :-X

Don't re-write old code. Write it correctly in the first place. If it turns out not to be flexible or adaptable enough then you'll have to redesign it from scratch.

edit: damn my post button skills are too fast!
I forgot to add that if you have crappy code, or you try to extend it too many times you could end up with vista! :o

Thomas Fjellstrom
Quote:

If it turns out not to be flexible or adaptable enough then you'll have to redesign it from scratch.

That is what is implied by "rewriting old code". You would NEVER rewrite an app the same way twice. Unless it was a perfectly coded piece of ART that you somehow lost the source to.

Matt Smith
Quote:

It has a rather large allegro dialog struct and the indices are somehow wrong. Months ago I added something to the dialog and never got it ready. Now I need to make it ready and I have a hard time debugging it. It compiles and runs, but doesn't do the thing it should. And it is because indices are wrong.

I use defines for this, although enums would be better (I have been doing this since before I knew C thoroughly) This list of defines (or enum) needs to kept manually in sync with changes to the dialog, but it saves shlepping through the rest of the code. Hopefully, the dialog editor in DevAlleg will automatically maintain this list by associating the 'ID' with the relevant index. This has stalled because I want to parse the entire file for everything, because dlg's way of doing it does not preserve comments etc., and does not have automatic run-time initialisation code refactoring.

1 
2DIALOG prjopts_dialog[] =
3{
4 /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) */
5 { d_shadow_box_proc, 0, 0, 600, 400, 255, 0, 0, 0, 0, 0, NULL },
6 { d_ctext_proc, 300, 4, 200, 16, 255, 1, 0, 0, 0, 0, "Project Options" },
7 
8 { d_text_proc, 20, 17, 130, 10, 255,1, 0, 0, 0, 0, "Project " },
9 { d_list_proc, 84, 14, 200, 16, 255,0, 0, D_EXIT, 0, 0, &project_name_getter},
10 
11 
12 { mage_tabbar_proc, 10, 30, 580, 350, 12, 66, 0, 0, 0, 0, &dev1_prjopts_tabbar}, /* 1 */
13 
14 { mage_button_proc, 400, 384, _WB, _HB, 255, 12, 'c', D_EXIT, 0, 0, "&Cancel" },
15 { mage_button_proc, 500, 384, _WB, _HB, 255, 12, 'o', D_EXIT, 0, 0, "OK" },
16 
17/* General Tab */
18 
19 { d_shadow_box_proc, 20, 44, 560, 330, 255, 0, 0, 0, 0, 0, NULL },
20 
21 
22/* Directories Tab */
23 { d_shadow_box_proc, 20, 44, 560, 330, 255, 0, 0, 0, 0, 0, NULL },
24 
25 { d_text_proc, 30, 50, 130, 16, 255,1, 0, 0, 0, 0, "Project name" },
26 { d_edit_proc, 30, 50, 400, 16, 255,0, 0, 0, sizeof(prjo_name),0,prjo_name},
27 { d_button_proc, 440, 60, 80, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
28 
29 { d_text_proc, 30, 80, 130, 16, 255,1, 0, 0, 0, 0, "File" },
30 { d_edit_proc, 30, 90, 400, 16, 255,0, 0, 0, sizeof(prjo_fname),0,prjo_fname},
31 { d_button_proc, 440, 90, 80, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
32 
33 { d_text_proc, 30, 110, 130, 16, 255,1, 0, 0, 0, 0, "Working Directory" },
34 { d_edit_proc, 30, 120, 400, 16, 255,0, 0, 0, sizeof(prjo_workingdir),0,prjo_workingdir},
35 { d_button_proc, 440, 120, 80, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
36 
37 { d_text_proc, 30, 140, 130, 16, 255,1, 0, 0, 0, 0, "Local Root" },
38 { d_edit_proc, 30, 150, 400, 16, 255,0, 0, 0, sizeof(prjo_localroot),0,prjo_localroot},
39 { d_button_proc, 440, 150, 80, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
40 
41 { d_textbox_proc, 80, 180, 200, 120, 12, 66, 0, 0, 0, 0, dev1prjo_target_text},
42 
43 
44/* Compilers Tab */
45 { d_shadow_box_proc, 20, 44, 560, 330, 255, 0, 0, 0, 0, 0, NULL },
46 
47 { d_list_proc, 80, 50, 200, 16, 12, 66, 0, 0, 0, 0, &edit_filetype_getter},
48 { d_list_proc, 80, 70, 200, 16, 255,0, 0, D_EXIT, 0, 0, &mage_fontname_getter},
49 
50 
51 { mage_check_proc, 40,160,120,10, 255,0, 'r',D_EXIT, 1, 0 , "show &ruler"},
52 { mage_check_proc, 40,170,120,10, 255,0, 't',D_EXIT, 1, 0 , "show &tabs"},
53 { mage_check_proc, 40,180,120,10, 255,0, 's',D_EXIT, 1, 0 , "tabs to spaces"},
54 { mage_check_proc, 40,190,120,10, 255,0, 'l',D_EXIT, 1, 0 , "show &linefeed"},
55 { mage_check_proc, 40,200,120,10, 255,0, 'e',D_EXIT, 1, 0 , "show lin&ewrap"},
56 { mage_check_proc, 40,210,120,10, 255,0, 'w',D_EXIT, 1, 0 , "show &wordwrap"},
57 { mage_check_proc, 40,220,120,10, 255,0, 'n',D_EXIT, 1, 0 , "show line &numbers"},
58 
59/* Data Tab */
60 
61 { d_shadow_box_proc, 20, 44, 560, 330, 255, 0, 0, 0, 0, 0, NULL },
62 
63 { d_text_proc, 30, 50, 140, 16, 255,1, 0, 0, 0, 0, "Data Plugins" },
64 { mage_check_proc, 40,60,120,10, 255,0, 'd',D_EXIT, 1, 0 , "&dialog editor"},
65 { mage_check_proc, 40,70,120,10, 255,0, 'm',D_EXIT, 1, 0 , "&menu editor"},
66 { mage_check_proc, 40,80,120,10, 255,0, 'b',D_EXIT, 1, 0 , "&bitmap editor"},
67 { mage_check_proc, 40,90,120,10, 255,0, 't',D_EXIT, 1, 0 , "&toolbar editor"},
68 { mage_check_proc, 40,100,120,10, 255,0, 'w',D_EXIT, 1, 0 , "dra&wing editor"},
69 { mage_check_proc, 40,110,120,10, 255,0, 'h',D_EXIT, 1, 0 , "&html editor"},
70 { mage_check_proc, 40,120,120,10, 255,0, 'a',D_EXIT, 1, 0 , "&audio editor"},
71 { mage_check_proc, 40,130,120,10, 255,0, 'u',D_EXIT, 1, 0 , "d&uh editor"},
72 
73 
74/* Libraries Tab */
75 
76 { d_shadow_box_proc, 20, 44, 560, 330, 255, 0, 0, 0, 0, 0, NULL },
77 
78 { d_text_proc, 30, 50, 130, 16, 255,1, 0, 0, 0, 0, "Libraries" },
79 { d_edit_proc, 30, 60, 200, 16, 255,0, 0, 0, sizeof(target_pathname),0, target_pathname},
80 { d_button_proc, 440, 60, 80, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
81 
82 { d_text_proc, 30, 80, 130, 16, 255,1, 0, 0, 0, 0, "Linker Options" },
83 { d_edit_proc, 30, 90, 200, 16, 255,0, 0, 0, sizeof(target_pathname),0, target_pathname},
84 { d_button_proc, 440, 90, 80, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
85 
86 
87/* Libraries Tab */
88 
89 { d_shadow_box_proc, 20, 44, 560, 330, 255, 0, 0, 0, 0, 0, NULL },
90 
91 { d_text_proc, 30, 50, 130, 16, 255,1, 0, 0, 0, 0, "Libraries" },
92 { d_edit_proc, 30, 60, 400, 16, 255,0, 0, 0, sizeof(prjo_libs),0, prjo_libs},
93 { mage_button_proc, 440, 60, 48, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
94 
95 { d_text_proc, 30, 80, 130, 16, 255,1, 0, 0, 0, 0, "Linker Options" },
96 { d_edit_proc, 30, 90, 200, 16, 255,0, 0, 0, sizeof(prjo_linkopts),0, prjo_linkopts},
97 { mage_button_proc, 440, 90, 48, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
98 
99 { mage_check_proc, 40,110,120,10, 255,0, 's',D_EXIT, 1, 0 , "Link with Standard Libs"},
100 { mage_check_proc, 40,120,120,10, 255,0, 'a',D_EXIT, 1, 0 , "Link with Allegro 4"},
101 { mage_check_proc, 40,130,120,10, 255,0, 'g',D_EXIT, 1, 0 , "Link with AllegroGL"},
102 { mage_check_proc, 40,140,120,10, 255,0, 'm',D_EXIT, 1, 0 , "Link with Mage"},
103 
104 
105/* Target Tab */
106 
107 { d_shadow_box_proc, 20, 44, 560, 330, 255, 0, 0, 0, 0, 0, NULL },
108 
109 { d_textbox_proc, 80, 120, 200, 120, 12, 66, 0, 0, 0, 0, dev1prjo_target_text},
110 { d_text_proc, 30, 50, 140, 16, 255,1, 0, 0, 0, 0, "Target Type" },
111 { d_list_proc, 160, 50, 200, 16, 255,0, 0, D_EXIT, 0, 0, &target_filetype_getter},
112 
113 { d_text_proc, 30, 70, 140, 16, 255,1, 0, 0, 0, 0, "Filename" },
114 { d_edit_proc, 160, 70, 200, 16, 255,0, 0, 0, sizeof(prjo_target_fname),0, prjo_target_fname},
115 { mage_button_proc, 440, 70, 48, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
116 
117 { d_text_proc, 30, 90, 140, 16, 255,1, 0, 0, 0, 0, "Icon" },
118 { d_edit_proc, 160, 90, 200, 16, 255,0, 0, 0, sizeof(prjo_icon_fname),0, prjo_icon_fname},
119 { mage_button_proc, 440, 90, 48, 16, 255,12,'o',D_EXIT, 0, 0, "browse" },
120 
121 
122 { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL }
123};
124 
125 
126 
127#define PRJOPTS_CANCEL_BUTTON 3
128 
129#define PRJO_GEN_PANE 7
130#define PRJO_DIR_PANE 8
131#define PRJO_BROWSE_FILENAME 14
132#define PRJO_BROWSE_WORKINGDIR 17
133#define PRJO_BROWSE_LOCALROOT 20
134 
135#define PRJO_CMP_PANE 22
136#define PRJO_LINK_PANE 32
137#define PRJO_GFX_PANE 42
138#define PRJO_SOUND_PANE 49
139 
140#define PRJO_TARGET_PANE 60
141#define PRJO_BROWSE_TARGET 66
142#define PRJO_BROWSE_ICON 69
143 
144#define PRJO_E_PANE 70

Kitty Cat
Quote:

I use defines for this, although enums would be better (I have been doing this since before I knew C thoroughly) This list of defines (or enum) needs to kept manually in sync with changes to the dialog, but it saves shlepping through the rest of the code.

This is what I do in my ogg_encoder tool for apeg (which relies quite a bit on object "positions").

1static DIALOG main_dialog[] =
2{
3 { .proc=d_agup_clear_proc, .w=SW, .h=SH },
4 { .proc=d_agup_menu_proc, .x=-1, .y=-1, .dp=main_menu },
5#define ENCODE_BTN 2
6 { .proc=d_agup_button_proc, .x=SW/4-32, .y=448, .w=64, .h=16, .flags=D_EXIT, .dp="Encode" },
7#define TEST_BTN (ENCODE_BTN+1)
8 { .proc=d_agup_push_proc, .x=SW*2/4-32, .y=448, .w=64, .h=16, .dp="Test", .dp3=simple_player
9 },
10#define CANCEL_BTN (TEST_BTN+1)
11 { .proc=d_agup_push_proc, .x=SW*3/4-32, .y=448, .w=64, .h=16, .dp="Quit", .dp3=quit },
12#define MAIN_AREA_END (CANCEL_BTN)
13 
14#define VIDEO_AREA_START (MAIN_AREA_END+1)
15 { .proc=d_agup_window_proc, .x=6, .y=16, .w=628, .h=164, .dp="Video Options" },
16#define VIDEO_RADIO_BTN(x) (VIDEO_AREA_START+1+(x))
17 { .proc=d_agup_radio_proc, .x=14, .y=40, .w=96, .h=10, .d1=1, .flags=D_EXIT,
18 .dp="Video file" },
19 { .proc=d_agup_radio_proc, .x=120, .y=40, .w=96, .h=10, .d1=1,
20 .flags=D_EXIT, .dp="Video pipe" },
21 { .proc=d_agup_radio_proc, .x=224, .y=40, .w=120, .h=10, .d1=1,
22 .flags=D_SELECTED|D_EXIT, .dp="Disable Video" },
23#define VIDEO_BROWSE_BTN (VIDEO_RADIO_BTN(0)+3)
24 { .proc=d_agup_push_proc, .x=28+522, .y=53, .w=64, .h=16,
25 .flags=D_DISABLED, .dp="Browse", .dp3=file_browser },
26 { .proc=d_agup_edit_proc, .x=32, .y=54, .w=522-8, .h=14, .flags=D_DISABLED,
27 .d1=sizeof(video_fname)-1, .dp=video_fname },
28...

That way if I add or remove a widget, I just need to modify the one macro below it, and all the macros in the dialog remain in sync.

GullRaDriel
Bruce Perry said:

Ta gueule.

Hey Bruce, do you have a problem ? Even if is your French is good, it is not good enough to crush me at French bashing, baby :P .

Now, for your French practicing, a little bit of informations:
Reste cool, je n'ai agressé personne. Quand bien même PossumDude0 se sentirais blessé, se serait à lui de le montrer, et non à toi. "Ta Gueule" est une expression qui est loin d'être poli. A la limite tu aurais posté un "Ta Gull" et cela aurait pu passer pour une taquinerie. Pour ta culture personnelle, la définition suivante convient parfaitement au mot poli: Dont le comportement, les manières, le langage sont conformes aux règles de la bienséance, au respect des convenances. Ce qui est loin d'être le cas de ta remarque.

Goalie Ca said:

Tu peux la fermer? :-X

That is the difference between the French and Canadian speak ;-) You Canadian are more courteous than us the French ;-) ( and even more courteous than people learning French. )

Mark Oates: sorry. I did not want to derail the thread.

PossumDude0: Were you really shocked ? If so I tell you now there was no reason as it was joking. If not, heh, have a nice day.

Now, back on the thread rail:
As Johan, I am also using dlg from Julien Cugnieres, it fits my needs perfectly.
You should really give it a try.

EDIT:
A simple copy-paste in a file with Matt dialog give me this:
{"name":"591667","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/b\/2bc4bad99c0f4f66dfa4785659d86082.jpg","w":646,"h":505,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/b\/2bc4bad99c0f4f66dfa4785659d86082"}591667
_

Bruce Perry

Whoa. All that reply to a post with two words in it! :o

Your post just didn't seem funny. It seemed little more than an insult. If I can take it wrongly, then a lot of people could. It was worth my posting that, because now it's painfully clear you meant it as a joke :)

(I wondered if you were going to flaunt your native French at me :P)

Goalie Ca

In canada we have a saying for "english people" who just don't get it...

Quote:

Tête-Carré

GullRaDriel

My my my, Bruce. We misunderstood each other ! :'(

Lets say it is OK now :P

While I wrote my answer I was remembering the long talk we had on irc some times ago, and your post made me a little bit angry ( as I was really joking PossumDude0 this time ).

Bwahbwahbwahbwah, everything is under control now :-)

:D

Neil Black

I just noticed something in my code. It looks like KEY_PLUS_PAD will decrease the speed, and KEY_MINUS_PAD will increase it!?

HoHo

It is quite logical in my oppinion, you are changing friction. When you lessen the friction your whatever_it_is will move faster.

Neil Black

Oh yeah, I was decreasing the delay, which increased the speed... brain fart!

Trezker

Halflife sourcecode:
float m_frictionFraction; // Sorry, couldn't resist this name :)
dlls\triggers.cpp (line 56)

BAF

PossumDude - do you not know how to clean up after yourself? Your program seems to leak a nice chunk of memory, depending on the size of those bitmaps.

Neil Black

1) I don't really understand what leaking memory is, but I can tell it's important and I need to learn it, it's Google-time!

2) That program, as I have already stated, need rewriting. It is also unfinished, because they cut off my electricity when I had barely gotten things displaying properly. But I'm getting my electicity back on next week! I hope...

bamccaig

Memory leaks when memory is reserved by a program and never released back to the operating system. When the program closes the operating system doesn't know the memory is free so it will not give it to another program. That memory is essentially unusable until the computer restarts (or possible the operating system determines that it's not in use, but I don't think this happens).

**See following posts**

In other words, when you create a variable in your program, your program has to request memory from the operating system, which controls the entire computer. If it has the memory available it will probably grant it to you. If not, depending on the system, it will probably somehow message your program to indicate that there isn't enough memory. If your program doesn't handle this (probably an exception) your program will otherwise crash.

Once the operating system grants your program access to memory no other program should be allowed to use it (that is dependent on the operating system, but I assume XP or Vista do this; and I'm very certain that *nix systems do). That will ensure that another program doesn't overwrite a variable on you and mess up your program.

When your program is finished with memory, it should release it so the operating system can give it to some other program.

A simple example:

1class Object
2{
3private:
4 int mintX;
5 int mintY;
6public:
7 int X(void)
8 {
9 return mintX;
10 }
11 
12 void X(int intX)
13 {
14 mintX = intX;
15 }
16 
17 int Y(void)
18 {
19 return mintY;
20 }
21 
22 void Y(int intY)
23 {
24 mintY = intY;
25 }
26}
27 
28int main(void)
29{
30 int *intPointer = NULL; // Stores a memory address, not an int.
31 Object *objPointer = NULL; // Stores a memory address, not an Object.
32 
33 intPointer = new int[1]; // Create a new int and point to it with intPointer.
34 objPointer = new Object(); // Create a new Object and point to it with objPointer.
35 
36 /*
37 * What we have done is create two variables that store memory addresses. Then
38 * we instantiated an int and an Object and stored their memory addresses in
39 * intPointer and objPointer. If we refer to intPointer we are actually going
40 * to be getting a memory address, not the integer it points to. However, if
41 * we use the * operator with it, which translates to "the value pointed to
42 * by" we can access the actual integer. Therefore, *intPointer becomes "value
43 * pointed to by intPointer".
44 */
45 *intPointer = 5; // Store 5 in the memory space pointed to by intPointer.
46 
47 /*
48 * With objPointer we can reference it and it's properties the same way. For
49 * example, *objPointer.X() would return the value stored in mintX of
50 * our Object instance, which is pointed to by objPointer. We can also use the
51 * -> operator to get access to the members and methods of an object pointer,
52 * which will allow us to avoid using *. For example, objPointer->X() is the
53 * same as *objPointer.X().
54 */
55 
56 /*
57 * Set the mintX property of the Object instance pointed to by objPointer to
58 * 5. Set the mintY property of the Object instance pointed to by objPointer
59 * to 3.
60 */
61 *objPointer.X(5);
62 *objPointer.Y(3);
63 
64 /*
65 * Set the mintX property of the Object instance pointed to by objPointer to
66 * 6, this time using the -> operator instead of * and . operators. Set the
67 * mintY property of the Object instance pointed to by objPointer to 8, also
68 * using the -> operator instead of * and . operators.
69 */
70 objPointer->X(6);
71 objPointer->Y(8);
72 
73 /*
74 * Now lets illustrate the point to be made: MEMORY LEAKS. When a variable
75 * goes out of scope (such as the end of a function or the program) there are
76 * instructions to release the memory used by your program. I assume the
77 * compiler takes care of this, but I'm not completely sure. Anyway, that is
78 * when memory used by your variables are released to the operating system.
79 * However, when a pointer is released the memory being released is the memory
80 * that holds the memory address of the data, not the actual data. As a
81 * result, the actual memory used to store the data is orphaned. The reference
82 * is released (the pointer) and that memory is unusable until the program
83 * exits.
84
85 * Something to note is pointer scope: a pointer in a function will be
86 * released at the end of the function, which means you lose the address it
87 * points to and that memory is again "leaked". As a result, before your
88 * pointers go out of scope you should always release the data they point to,
89 * or pass the address somewhere else so you can continue to use it and
90 * release it later. Using malloc() to allocate your memory you should use
91 * free() to release it. Using the new operator to allocate your memory you
92 * should use the delete operator to release it.
93 */
94// delete intPointer; // Releases the memory pointed to by intPointer. I've
95 // commented it out to actually illustrate a memory leak.
96 intPointer = NULL; // We are overwriting the address with NULL (0 or \0) to
97 // illustrate that is no longer in use. Doing this before
98 // you release the data is bad because you're leaking that
99 // memory. Because we have no other reference/pointer to
100 // the instance and we never released it (because the
101 // delete statement is commented out) we are causing a
102 // memory leak.
103 
104 delete objPointer; // Releases the memory pointed to by objPointer.
105 objPointer = NULL; // We can no longer use the address that it points to
106 // because the operating system owns it again, so we set
107 // it to NULL (0 or \0) just to illustrate that.
108 
109 return 0;
110 
111 /* End of our program. Oooops! We leaked the integer instance. We actually
112 * leaked it when we set intPointer to NULL because we overwrote the address,
113 * making it impossible to release it because we don't know where it was.
114 * Apparently the operating system will free the resources when our program
115 * ends so it shouldn't be a problem in this program (apparently).
116 */
117}

Sorry if I went over too much, but I figured there's no point explaining just the new and delete stuff if you, or somebody else stumbling across this thread, doesn't understand a bit about pointers and memory addresses.

Thomas Fjellstrom
Quote:

That memory is essentially unusable until the computer restarts (or possible the operating system determines that it's not in use, but I don't think this happens).

Any decently modern operating system will free the memory once a program closes.

The main problem comes when your program runs for more than a few seconds, if you never free memory that you allocate, it'll keep building up and eventually run out of memory, cause the OS to start swaping stuff to disk, and then things get really slow.

If you new/malloc/create_bitmap/load_bitmap you must free the memory (delete/free/destroy_bitmap/destroy_bitmap) when you are done with it.

bamccaig
Thomas Fjellstrom said:

Any decently modern operating system will free the memory once a program closes.

Ah yes. So I guess when your program exits the operating system should be able to clean up any unreleased memory (makes logical sense). However, during the execution of your program you can hog a ton of memory that's not even in use anymore if you don't release it properly.

Thomas Fjellstrom
Quote:

However, during the execution of your program you can hog a ton of memory that's not even in use anymore if don't release it properly.

I think I just said that. Did you read the entire post?

bamccaig
Thomas Fjellstrom said:

I think I just said that. Did you read the entire post?

Shit no. Do you think I have time to read a fucking novel like that! Tone it down a bit.

Obviously, you're gonna freak out on that so allow me to apologize. That last post was really my "I'm embarassed as I should have known that and yes, I think he's right" post.

Thomas Fjellstrom said:

The main problem comes when your program runs for more than a few seconds, if you never free memory that you allocate, it'll keep building up and eventually run out of memory, cause the OS to start swaping stuff to disk, and then things get really slow.

I thought that the operating system usually throws large idle programs out to swap in an attempt to free memory ahead of time and also so it can load things into memory that it predicts will be needed soon. I'm not really an expert on operating systems though. Anybody know of a good OS writing tutorial? {smiley}

Thomas Fjellstrom
Quote:

Shit no. Do you think I have time to read a fucking novel like that! Tone it down a bit.

Uh, You seem to be upset :o

Quote:

Obviously, you're gonna freak out on that so allow me to apologize.

I freaked out? It was a simple question.

Quote:

I thought that the operating system usually throws large idle programs out to swap in an attempt to free memory ahead of time and also so it can load things into memory that it predicts will be needed soon.

Sorta. It mainly swaps when asked. I something needs ram, and theres no physical ram left, the OS will swap some pages to free enough (and maybe more) for the request.

Linux will end up swaping some way underused programs if they almost are never used, and the file/disk cache is well used. (it'll swap stuff to make room for disk cache)

edit: Also, Vista has added proper file/disk caching as well, so I'll bet it will do the same. Of course it also added extra app caching features with flash drives and whatnot.

bamccaig
Thomas Fjellstrom said:

Uh, You seem to be upset

No, that was said sarcastically. I would have made it more obvious, but I assume it's still EFW. When does that end exactly?

Thomas Fjellstrom said:

I freaked out? It was a simple question.

I didn't want you to freak out because I referred to your post as a novel. I'm sure regular users have realized by now that I often have long replies.

And it sounded like you thought I was trying to take credit for your answer. I just wanted to clear it up... I can see now that it's gonna take a while.

Thomas Fjellstrom
Quote:

I can see now that it's gonna take a while.

Not at all. I just have a small beef with people not reading posts. :)

Joel Pettersson
Quote:

Any decently modern operating system will free the memory once a program closes.

Indeed; hence, (warning; evil suggestion) you can often greatly optimize shut-down times of programs using a lot of dynamically allocated memory - especially ones with a ton of destructors or other cleanup functions to be called - by simply doing a quick-and-dirty exit, focusing proper memory management on the parts of the program active during its lifetime.

bamccaig
Joel Pattersson said:

...by simply doing a quick-and-dirty exit,...

My concern is which is faster and more reliable: explicitly cleaning things up or the operating system cleaning up the mess you leave behind?

Thomas Fjellstrom

The os can probably just defer the freeing of stuff. If the program isn't meant to do anything fancy, and doesn't run long, you don't NEED to free any of the mem yourself.

Unfortunately not all resources are memory, theres also threads, Window Handles, GDI resources, X11 Resources, and occasionally, if you don't free those sorts of things, they may not be freed correctly. Win 9x has always had a NASTY problem with not freeing ANY GDI, KERNEL32, or USER32 handles by itself when an app crashes or even just forgets to free stuff. I've you've ever seen the GUI turn into a copy of Win3.1, you just ran out of GDI handles.

Its a lot harder to do the same in 2k and XP though, since its based on a much better kernel.

Personally, not freeing stuff feels dirty, and wrong. I prefer to do things the right way when I can.

bamccaig
Thomas Fjellstrom said:

Personally, not freeing stuff feels dirty, and wrong. I prefer to do things the right way when I can.

I'm the same way. For me it's a compulsion. That's also why I edit forum posts so often. If I don't do things "perfectly" I'm compelled to fix them.

(I've been drinking Canadian beer... I don't know if I'm making spelling/grammar errors right now... :'()

Thomas Fjellstrom
Quote:

If I don't do things "perfectly" I'm compelled to fix them.

Yeah, I usually try, but some of the time I don't even notice the errors, and anal people like to throw witty remarks around about my errors (so sue me if I have a mild case of dyslexia and really bad recall).

BAF
Quote:

Its a lot harder to do the same in 2k and XP though, since its based on a much better kernel.

I actually have hit that before (tons of leaked GDI handles in XP). XP tops out around 32,000 handles, and when they get leaked by various crappy programs over the course of 8-10 weeks (my average reboot cycle), you can end up having those issues. However, it's normally programs that stay running. Also, if you log out/back on on XP, it seems to clean out a bunch of those leaked GDI handles.

[edit]
X-Chat falls under the crappy programs category. It only has 129 GDI handles, however, somehow it has managed ot eat up 5,017 handles and 1,152 threads.

Joel Pettersson
Quote:

My concern is which is faster and more reliable: explicitly cleaning things up or the operating system cleaning up the mess you leave behind?

The OS will catch it all, so the reliability is not affected when you perform a given part of the memory freeing manually. And doing it manually often requires many relatively costly freeing calls - and often executing much other code, particularly in heavily OO-structured programs where multiple destructurs may be called for each of many objects. Slightly slower at best, much slower at worst.

Quote:

Unfortunately not all resources are memory, ...

And there you can have reason to do things "properly". When it is guaranteed that avoiding running code won't do any harm, though, it makes no more sense to put it in than adding a sleep call.

Quote:

If I don't do things "perfectly" I'm compelled to fix them.

That goes for me as well, only what I see as being "perfect" is different; I go to great (sometimes semi-obsessive) lengths to avoid redundancy and overhead.

bamccaig
Joel Pettersson said:

That goes for me as well, only what I see as being "perfect" is different; I go to great (sometimes semi-obsessive) lengths to avoid redundancy and overhead.

Yeah, I would say it is semi-obsessive of me as well. Refer to the Off-Topic thread started a few weeks ago titled Post a weird fact about you...........

Or for a direct link to those relevant posts:

What I see as being perfect is probably relative to the situation.

BAF

You are OCD, yet you still write unclean PHP code? That doesn't sound right to me.

bamccaig
BAF said:

You are OCD, yet you still write unclean PHP code? That doesn't sound right to me.

I've never been diagnosed. As far as unclean PHP code, that depends on your definition of unclean. To me it was cleaner. Dynamically-typed langauges are considered unclean to me, so I only use them when I have to.

Thread #590597. Printed from Allegro.cc