Tuesday, March 23, 2010

Friday, November 20, 2009

We all know that stl is great. It has fantastic daily-used data structures, algorithms and string. We've learned that we should use it instead of array or char*. Yeah, it enables us to avoid reinventing the wheel! and save lots of lots of time.

So what's the problem?

It's allocating memory.

It does NOT mean stl sucks because it needs heap memory. There is no problem as long as we know what we're doing. It's allocating memory when you use them and this could be an issue when you find your program is not fast enough and the reason for that is memory fragmentation, you know enough memory space but fragmented as hell.

Now, I'm sure you see the problem. It will create countless small temporary memories and definitely affect overall memory usage. I know some of you use your own "new/delete" or "malloc/free" but that doesn't make big difference since it's going to make your memory pool fragmented and you need time for de-fragmenting to find enough space.

So what should we do?

Well, here are my own tips.

1. Try to reuse stl objects as much as possible.- Put them into a class as member variables.- "static" can be one solution if you are free from threads.- Always "reserve" their space in advance to avoid unnecessary allocations.

2. For temporary local objects, try to use "stack" objects, not heap ones.- Use array if you know the maximum size.- Try boost::array if you really don't like to see brackets.- stl::string is too tempting but for temporary strings, char[] can be much more efficient.

That's it.

I think this is my last post for 2009. I was reading my old postings last night and I really like them. lol. I sometimes learn many things from what I've written. In 2010 (oh wow! cannot believe I will live in 2010!), hope I will be a slightly better programmer than now.

Sunday, October 25, 2009

Last week, I had a task for fixing a bug related to a library from outside my company.

First of all, I found that we were using its functions in a wrong way so I made some changes to correct it. Of course, the way I fixed it is from a document of the library. It resolved the issue and worked well in my machine. But, it turned out that a few machines had another problem with my changes.

Luckily, we have source code of the library so I was able to look into it to find my mistake. However, I realized that they made a wrong assumption about some values and it caused the problem. Since I had the source code, I fixed the wrong assumption, compiled it and produced one dll file for our project. It was working just fine on my test machine as well as my own pc. Yeap, I have two machines and three monitors at work. :)

Anyway, the happy moment didn't last long though. Our build machine reported that it failed to test its final build with my newly produced dll. Our game couldn't even start with it but just showed a very simple system error dialog with a strange hexadecimal address.

From my programming experience, I know that this kind of problems could take forever if I just try to solve it by myself. I quickly noticed that I needed someone. Someone knows our project very well and understands my task. Yeah, I asked my lead programmer to help me.

We agreed that the changes I made is the proper way to do my task. The problem was why the dll working in my pc didn't work in our build machine. We found that the dll didn't work on his machine either. He was for sure that my machine had something different and it produced the dll having incorrect dependency for the other machines. So, what is it?

It took almost four hours us to find the reason. It was not because my machine is using 64 bits Windows and the build machine is using 32 bits one. It was not because of the project setting for the library. It was not even because I used their static version of project to produce dll. (Another good example why naming is so important. They named the project "static something" but it actually created a dll, "dynamic" linked library.)

When my lead and I almost gave up, I found that my Visual Studio 2005 had all the latest updates and the one in build machine didn't. It's just my habit that I runs "Windows Updates" or "Microsoft Updates" every day when I have a short break. This habit had never made any problems until this one happened.

It turned out that one of the security updates for VS2005, KB971090, made the problem. From google, I found that dlls compiled with the security update (KB971090) are not compatible with exe files compiled without it, if the machine which runs the exe doesn't have the update. To solve this problem, I simply uninstalled it instead of following the long solution which you might want to take if you have the same problem. I should mention that you need to check "View installed updates" in control panel to uninstall it. Yeah, it sucks.

Okay, here is what I've learned.

Keep the exactly same build environment with the other programmers including a build machine. Don't blindly get all updates without checking them.

Sunday, October 11, 2009

Last week, I was reading src code and found that it used zero as an initial value for a handle.

MY_HANDLE handle = 0;

But, it also used INVALID_HANDLE for the same purporse.

class A{public: A() : m_handle(INVALID_HANDLE) {}

private: MY_HANDLE m_handle;};

Well, I thought that INVALID_HANDLE was zero. However, it turned out that it's not. INVALID_HANDLE was defined with some weird value. Anyway, the exact value is not important. The importatnt thing is how I know what should be an initial value for MY_HANDLE?

Suprisingly, I'm told that some code use INVALID_HANDLE as a VALID value and there is a legitimate situation that a handle of an object is INVALID_HANDLE. Wow. Okay, so the real invalid value which is supposed to be used as an initial value is zero, not INVALID_HANDLE.

Yeah, I know this kind of things always happens in real world as we always see this comment;"Fix me!". But, before we fix this, I'd like to ask this question.

How can we communicate with other programmers who will work with the code we write in future? More specifically, how can we show our assumption in the code? Or, do we even need to do it? why?

Why?

Because we are all different humans. All of us have an unique appearance, personality and assumption. It's perfectly fine with having your own assumption like your own habits. But, there is no guarantee that your assumption makes sense to others. That's why we should say it very loudly whenever we assume something.

How?

First of all, name it correctly. If it is a function returning a size of array, name it "GetSizeOfArray()". If it's a value containing your grade, name it "myGrade". If it's a constant value for an invalid handle, name it "INVALID_HANDLE" and use it as it is called. You can follow your "common" sense but don't hesitate to spend enough time naming something.

Second, use assertion whenever you can. It can correct your assumption quickly if it is wrong, it is crystal clear for other programmers and you can turn it off easily so that it doesn't cost anything in your retail build. Yeah, it's cheap (almost free) but powerful enough to save your project.

One thing I should mention about assersion is that you should not mix assertions with error handling. If there is an error and you need to handle it, just handle it without writing asserion.See the below code.

assert(pRect != NULL);if(pRect != NULL) pRect->Draw();

The code says two things at the same time and they conflict with each other. It says "pRect must not be NULL." and "Call Draw() if pRect is not NULL". The latter one implicitly assumes that pRect might be NULL but the assertion says that pRect should never be NULL.

As Scott Meyers said that a female is either pregnant or she's not (in Effective C++), you should choose only one of them. Is it a part of program flow? or should it never happen? It's not possible to be partially pregnant.

Wednesday, September 23, 2009

Do you see the problem?Yes, only one character ruined the whole process.

It should be like this:

if(IsOK()) // if(!IsOK()) OkDoIt();else NoWait();

If the function containing this problem is rarely called, it's a time bomb inside a briefcase. Even after you recognize something is wrong, it's very hard to find this kind of mistakes because it's such a small one like off-by-one error. almost invisible. That's why small mistakes can be much more disastrous than big ones which are obviously more visible.

Since code like the above is usually written very quickly and unconsciously without thinking, if you don't have right habits, you will make a mistake. It brings me the idea I loved but, apparently, forgot.

Good programmers have good programming habits.

I will never use ! in if statement. ever.

One more thing to remember.

Test every changes you made.Every single line.

Of course, I tested my changes before I checked in it. However, there were many changes and I just quickly checked a normal flow without checking all the changes I made. I was focusing on bigger and more complicated changes. I should have kept my changes small and checked in them more frequently.

Anyway, it's absolutely my fault and I will never forget it.

I really don't think anything I do is a mistake. It could be if I didn't learn from it.

Thursday, September 3, 2009

I couldn't remember when it started but the compiler kept saying it could not find "ERROR" identifier. "ERROR" is supposed to be in wingdi.h like this.

#define ERROR 0

So I checked my wingdi.h file and it was like it.

#define LOG 0

It didn't take too much time to realize that wingdi.h was modified recently and all the other header files in the same folder had not been changed at all ever since the visual studio was installed.

I thought it was because I installed some other SDK or programs and I tried to figure out what made this situation. I had never imagined that I changed it. But, yes, I DID IT. OOPS!

I did "replace all" ERROR with LOG for my own project and wingdi.h was open at that moment. Visual Studio modified it and did not say anything when I saved and compiled it. There was no single error because I did not use ERROR in that project.

First of all, wingdi.h is NOT read-only so if you accidentally modify and save it, you won't see any warnning dialogs. Second, Visual Studio applies "replace all" for all opened files which are not in your solution even though you choose "Entire solution". Like the below picture.

So be careful otherwise you could waste hours even days like someone. =+=