Archive for category For Your Info
How to Copy a MS SQL 2005 Database
Posted by admin in For Your Info on December 17th, 2009
Problem: You want to make a copy of a Database to a Database on the same server. Useful if you want to run two sites that use the same database schema.
Solution: (Cleaned up version of http://skainsez.blogspot.com/2008/03/how-to-copy-sqlserver-2005-database.html)
Using Microsoft SQL Server Management Studio Express:
The first step I did was to create a new database on the server which would be the duplicate db.
For the purposes of this article let’s assume that my original db is called ‘Prod’ and my new db is called ‘Dev’.
So I right clicked on the ‘Databases’ folder in SSMSE (Sql Server Management Studio Express), chose ‘New Database . . .’ and created a new db called ‘Dev’.
Next up I right-clicked on the ‘Prod’ db (the one I want to duplicate) and chose ‘Tasks -> BackUp . . .’ I created a full backup of the db just to make sure I had the latest data. Once the backup had completed I then right-clicked on the ‘Dev’ database and chose ‘Tasks -> Restore -> Database . . .’
On the ‘General’ page I specified ‘Prod’ as my ‘From database’ and ‘Dev’ as my ‘To database’. Note that when you select the ‘From database’ the ‘To database’ value gets changed to that value. You need to make sure that the ‘To database’ value is actually the name of the duplicate database.
Next I clicked over to the Options page in the Restore dialog. Here I had to change the ‘Restore As’ names of the files in the file listing. By default they are the same as the ‘Original File Name’ values. But that would cause the backup to overwrite the original database.
What we want is to change the ‘Restore As’ names to be the names of the duplicate database’s files.
In this example I changed these two entries:
C:\Program Files\Microsoft SQL Server\MSSQL\data\Prod.mdf
C:\Program Files\Microsoft SQL Server\MSSQL\data\Prod_log.ldf
To:
C:\Program Files\Microsoft SQL Server\MSSQL\data\Dev.mdf
C:\Program Files\Microsoft SQL Server\MSSQL\data\Dev_log.ldf
Then, still on the Options page of the ‘Restore Database’ dialog, I checked the ‘Overwrite the existing database’ checkbox. Next just hit the ‘OK’ button and let it do its thing. The end result should be a duplicate of your original database.
FYI: Recursion
Posted by admin in For Your Info on May 4th, 2009
Source Code
fyi.recursion.cpp
Recursion is a tool in programming that is very useful for simplifying problems. The idea is that you solve for n+1 and then 1 thru n just work. A famous recursive problem in math is the Fibonacci Sequence.
1 | 0 1 1 2 3 5 8 ... |
You can see that the next number in the sequence is equal to the previous two numbers added together. So to express than in recursive terms we get:
1 | f(n+1) = f(n-1) + f(n-2) |
But, we have to consider two special cases. If n is 1 then n-2 is -1 and if n is 0 then n-1 is -1. So what we do to remedy this is define f(0) and f(1).
1 2 3 4 5 6 | int f(int n) { if(n<=0) //stopping condition return n; return f(n-1) + f(n-2); //recursive call } |
All recursive functions need a stopping function to avoid infinite recursion and a recursive call. Now that we’ve got a recursive method to find the nth Fibonacci number let’s examine a linear solution.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | int f(int n) { int x1, x2, x; if(n<=1) return n; x1 = 0; x2 = 1; for(int j=1;j<n;j++) { x = x1+x2; x1 = x2; x2 = x; } return x2; } |
Just like the recursive version we check to see if n<=1 and if so, we just return n. Otherwise we set x1 and x2 to the first two values of the Fibonacci sequence and then proceed to calculate the next values in the sequence up to the nth value.
Both of these functions return the same result but the first one is much simpler with only three lines of code. But now lets check the profiler results.
1 2 3 | Cycles to execute the function: Recursive: 268 Linear: 1574 |
The recursive version whomps the linear version for shear speed. But this isn’t the whole story. In our test code we find the first 35 Fibonacci numbers. Let’s see how many times each of these functions is called.
1 2 3 | Number of calls to the function: Recursive: 48,315,597 Linear: 35 |
Surprising? Well consider what the recursive function is doing. Since the previous values aren’t stored in memory, it has to calculate and recalculate the same Fibonacci number numerous times before it gets the final result for the current number. The linear version holds the previous two numbers in memory so they don’t need to be recalculated. So although recursion is a simplier means to describe certain problems it is not necessarily the faster way to solve a problem. In fact, it could take hours or days to calculate large nth values. And that’s if the program doesn’t crash from a stack overflow. The linear solution will take more time as numbers become larger but only by the time it takes to add two numbers and set three variables. It will take a very large value of n before the linear function begins to be noticably slower.
It’s very easy to just assume that recursion is the way to go. After all, it’s a neat and tidy little solution for many things. The fact of the matter is though, that for large recursive problems (such as finding the 100th Fibonacci number) recursion is a last resort. You want to avoid it when possible. At the very least you should implement a linear solution and a recursive function and see which works best. It’s a very rare problem that can only be solved recursively.
And that concludes this FYI. The purpose of these lesson was not only to show how recursion works but also demonstrate that neat and tidy is not always fast and efficient. You can’t just get stuck in a rut doing things a certain way because that’s the way you like to do it. You need to learn to explore new ways of doing things in order to maximize the efficiency of your code.
FYI: Precision Time
Posted by admin in For Your Info on May 4th, 2009
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class PrecisionTime { private: LARGE_INTEGER freq, ts; public: PrecisionTime() { QueryPerformanceFrequency(&freq); } //returns the current tick count in seconds double GetTime() { QueryPerformanceCounter(&ts); return (double)ts.QuadPart/(double)freq.QuadPart; } }; |
There are a number of ways to profile your code to check it’s performance. DevPartner Community Profiler is free and works really well for many things. However, while working on the code for doing a merge sort I found it incapable of reporting the time spent in the methods within the linked list handler class. So, I’m going to go ahead and cover the PrecisionTime class first. This bit of code is actually already contained in the coredx.cpp file. It’s used to calculate the frame rate.
The easiest way to check the execution time of a chunk of code is to use GetTickCount(). It’s amount of time in milliseconds since the system was started. One millisecond is 1/1000th of a second. This may sound very accurate but it’s the least accurate counter you can use. It has a very low actual resolution so your times can be off by several milliseconds. It’s useful for checking framerates but not useful for anything where you need high accuracy.
timeGetTime() returns the number of milliseconds since Windows was started. It’s more accurate than GetTickCount() but still not suited for high precision work. It has a resolution of about 5 milliseconds. What that means is that if you call timeGetTime() twice in less than 5 milliseconds you’ll probably get a wrong result. The longer the time frame between calls, the more accurate the results are.
QueryPerformanceCounter is simply the most accurate way to get the time. It has about a microsecond resolution. You can do quite a few things using the results of these functions but the main use of them is to find the time difference between two points in code. If a profiler isn’t available then this PrecisionTime class will suffice.
1 2 3 4 5 | //usage PrecisionTime pTime; double start = pTime.GetTime(); //do stuff double diff = pTime.GetTime() - start; |
diff will contain the number of seconds that have passed with a fractional portion to a high degree of accuracy. (ex: 0.00956)
And that concludes this quick FYI. You can just cut and paste the class into your code and it’s ready to be used. This FYI was just to give you a tool to help you find bottlenecks in your code and optimize. If you’re interested in how exactly everything works you can search for the keywords on Google and there are plenty of pages that go into more detail.
FYI: New Vs Malloc
Posted by admin in For Your Info on May 4th, 2009
Source Code
New and Delete are the C++ way to allocate and deallocate memory. Malloc and Free are the C way to do the same. You cannot mix and match these commands. If you allocate memory with New you must use Delete to free the memory. If you allocate memory with Malloc then you must use Free to free the memory. One disadvantage of New is that there is no way to reallocate memory. With Malloc you can use Realloc to extend the amount of memory quickly. With New you have to create a new chunck of memory with the modified size and then copy over the data from the original buffer and then delete the original buffer. This can be a slow process.
But, let’s look at raw time required to allocate and free memory using New/Delete and Malloc/Free.
time vs number of elements/1000
The tests consisted of allocating and deallocating memory using the C++ and C methods from 1000 elements to 1000000 elements in steps of 1000. This chart shows the average time to allocate and deallocate each element. You can see that New is faster than Malloc and that Free is faster than Delete. It would be nice if the pairs matched for the speed difference so that both New and Delete were faster. Let’s take a look at the combined time.
time vs number of elements/1000
You can see that although Free is faster, the total Malloc and Free time is slower than New and Delete combined. It’s not a huge speed advantage but when doing real time programming you need to choose the fastest method no matter how insignificant the advantage seems. Every little bit adds up to one big unecessary slow down.
One thing I’m surprised by is that when I was working on doing real time A* pathfinding for large maps, using New and Delete actually crippled the performance to where it was taking seconds to complete a path vs hundredths of a second. I’d have to review the code but having played around with this code a bit, it is possible to get Malloc to outperform New. This is why it’s necessary to be aware of multiple ways of doing things. In the case of this test code New/Delete is better but you should test performance for your specific code to verify you’re maximizing performance.
When considering which to use one of the key features of New and Delete is the ability to have a constructor and destructor in the class. With Malloc and Free, the constructor and destuctor of a class is never called. The result is that you must initialize the instance after the memory has been created by calling a method or supplying the code outside the class. If your code depends on constructors and destructors then New and Delete are your only choice and you’ll just have to give a bit on performance and make up for the hit somewhere else.
FYI: Linear Merge Sort
Posted by admin in For Your Info on May 4th, 2009
Source Code
fyi.mergesortlinear.cpp
As we learned when we first covered recursion using the Fibonacci Sequence, recursion carries with it an excessive amount of overhead. It may be an easier way to visualize and write a solution to a problem but it’s a last resort.
1 2 3 4 5 6 7 8 | void Sort() { head = MergeSort_Lin(head); cNode * current = head; while(current->next!=0) current=current->next; tail = current; } |
Just like the previous version of Merge Sort we’re using a level of abstraction to hide the complexities of sorting a linked list. Any one who uses this Linked List class we not need to supply anything to make it work. We start by calling MergeSort_Lin() which will do the actual sort and return the head of the list. For the recursive version we needed to supply the length of the list. In this version we don’t. The smallest two halves we can have consist of one node each. The first two nodes are just as good as any. In the recusive method we started with the two largest halves possible and worked down and then back up. In the linear function we start with the two smallest halves and work up. Once this function is done, again we need to find the tail of the sorted list since the old tail is most likely not at the end of the list.
A more detailed explanation of how this works can be found HERE. This is the page of the person who actually wrote this version. You can find it scattered around the internet in various projects. It’s even contained in GPL’d code which is interesting since the person who did that failed to leave the original copyright (BSD type license) intact. This function is in no way shape or form, GPL. You are free to do with it as you want and are not required to GPL (or BSD) your own code by using it.
Rather than regurgitating what the author wrote I’m going to focus instead on how much recursion gets stomped on by linear solutions.
1 2 3 4 5 6 7 8 9 | Number of Time to Sort(sec) elements Recursive Linear Linear/Recursive (%) 10 0.000068 0.000005 7.35 100 0.001053 0.000030 2.85 1000 0.015660 0.000330 3.13 10000 0.258348 0.042545 16.47 100000 3.741487 0.563338 15.06 1000000 N/A 7.230439 N/A |
Both functions have a larger increase in time as the number of elements increases.

Blue is the recursive method and pink in the linear function. At one million elements I just exited the recursive program after five minutes. What you don’t see from the time charts is how much memory is being used. The linear version doesn’t allocate any new memory or delete any old memory. This saves a significant amount of time as well as a significant amount of memory. Technically the merge sort is creating one new node for every old node deleted so memory isn’t being increased in any obvious manner. What’s causing the spike in memory usage is the recursive call. The stack is being used to keep track of the previous calls which is an expensive operation in terms of memory use and processor time.
This is not to say that recursion is pure evil but it is to say that it’s very bad. Sometimes a problem is easier to solve using recursion and it’s fine to use it as a crutch. But at some point, you should always find a linear solution to your recursive problems or you are just needlessly crippling your code.
Some people will claim that main() is a recursive call. After all, you keep repeating the same loop over and over.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //recursive main() void main() { //do stuff if(!done) main(); } //linear void main() { while(!done) { //do stuff } } |
A loop repeats a set of code. Recursion is calling a function from within the function being called. If you use recursion for your main loop you should uninstall your compiler and take up a new hobby.
And that concludes this lesson. You’ve now know how to sort a linked list using two different implementations of merge sort. There are other ways to sort a list and they may be covered at some point but they are slower than merge sort so they’re not much of a concern as our focus is real time so our goal is to find the fastest way to do things.
FYI: Merge Sort
Posted by admin in For Your Info on May 4th, 2009
Source Code
fyi.mergesort.cpp
Merge sort is most easily described in a recursive fashion. It works by breaking down a list into smaller and smaller halves and sorting them. It then combines the sorted lists into one large sorted list and returns the result.
1 2 3 4 5 6 7 8 | void Sort() { head = MergeSort_Rec(head, count); cNode * current = head; while(current->next!=0) current=current->next; tail = current; } |
In our cNodeHandler class we’ve added the above method. This is a level of abstraction to hide the complexity of sorting the list from the user of the class. You don’t need to know how Sort() works or supply any arguments. The first thing this method does is call MergeSort_Rec() with the head of the linked list and the number of nodes in the list. Since that method only returns the head of the list, it’s then necessary to run through the list to find the tail. What used to be the tail is now probably anywhere but the end of the list. We’re not taking any chances.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | cNode * MergeSort_Rec(cNode * top, int size) { cNode * first = 0; cNode * second = 0; cNode * hf = 0; cNode * hs = 0; int j; if (size <= 1) return top; //make a sublist "first" with one half of the nodes if(top!=0) { first = new cNode(top); hf = first; top = top->next; for(j=1;j<size/2 && top!=0;j++) { first->next = new cNode(top); first=first->next; top=top->next; } first = MergeSort_Rec(hf,(size/2)); } //make a sublist "second" with the rest of the nodes if(top != 0) { second=new cNode(top); hs=second; top=top->next; for(j=1;j<(size+1)/2 && top!=0; j++) { second->next = new cNode(top); second = second->next; top=top->next; } second = MergeSort_Rec(hs,(size+1)/2); } return MergeNodes(first,second); } |
If you would like a more detailed look at how Merge Sort works, take a look at this Homework Assignment which was issued in 1997. This stuff is ancient and still very relavent.
first and second are the tails of the first and second halves of the list. hf and hs are the heads of those lists. You can see that the stopping condition for this recursive function is if size is less than or equal to 1. If we have no nodes or 1 node then we can’t break the list in half so we just return the node that was passed in.
Next we have our recursive calls. What’s happening is that we first break down the first half of the list into successivly smaller halves. We then break down the second half of the list into successivly smaller halves. And finally we merge those halves together and return the result.
I’m not going to bother copying all the code of MergeNodes() here. This is the function which runs through first and second and generates a single sorted list. Since first and second are always sorted we just check the first node of each of the lists and whichever is supposed to come first, based on a logical condition statement, is added to the combined list. We then move forward in the list that had the selected node and repeat until both lists are exhausted. This sorted list is then taken up the previous recursive step and the process repeats until we have one sorted list with all the elements from the original list.
You should note that the original list is deleted as we go through and sort it. We are generating a whole new list with nodes of the same values as the old list. If we didn’t delete the nodes then we’d have a memory leak. It may be possible to simply “rewire” the original list rather than generating a new list but that is left as an exercise to the reader.
And that concludes this tutorial. You now know how to sort a linked list using recursion. But, as you may recall from our recursion tutorial, recursion is a slow way to do things. Yes this Merge Sort works and it’s pretty fast, but like all other recursive functions, a linear version would be significantly faster. And it just so happens we have such a version which we will cover in the next FYI.
FYI: Linked List Class
Posted by admin in For Your Info on May 4th, 2009

Source Code
fyi.linkedlist.cpp
There are very few things you should memorize when it comes to programming. How to set up a linked list is one of them. One of the pitfalls of learning how to use a linked list is getting the idea that it’s good for everything. It’s not. If you don’t know how many of something you’re going to have and don’t want to take a guess then you have two options. You can use a dynamic array or a linked list.
1 2 3 4 | //dynamic array int * a; a = new int[100]; delete [] a; |
A dynamic array gives you random access in that you can reference an element by it’s array number (i.e. int b = a[1]). The downside is that if you run out of space you have to reallocate all the current memory plus additional space to hold the additional items. This can become very time consuming.
A linked list on the other hand, does not allow random access. If you have 5 elements in the list and want to get to the 4th element you will have to start at the front of the list and traverse the list until you get to the 4th element. There are ways to speed this process up but they will not be covered in this lesson. The advantage over dymanic memory is that increasing the size of the list only involves allocating memory for the new element. No memory needs to be reallocated. This makes it very fast for adding elements. And, unlike a dynamic array, the time needed to allocate a new element will never increase as the list becomes larger.
So, when you are deciding between a linked list and a dynamic array it’s necessary to consider what you need. If you need random access then you would want to use a dynamic array. If you can get away with sequential access then a linked list would be a good choice.
In this tutorial we’re just going to cover a basic linked list and a linked list handler. The reason we’re going with a linked list is that any time we’re looking at object information we’re looking at all the objects. So we save nothing with random access.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class cNode { public: cNode * next; int x; cNode() { x = 0; next = 0; } cNode(cNode &node) { x=node.x; next = node.next; } }; |
There are two parts to this linked list class. The first part is the node itself. The node will contain all the information about our object. It also contains a default constructor to zero everything out and a constuctor which takes a node. The new node then copies all the values from the passed in node. Because this is all pointers you do not want to simply use an equals sign. You also want to always default pointers to 0 unless you have a known valid address or you will encounter problems later on.
1 2 3 4 | cNode(cNode &node) { this = node; //bad idea } |
You’ll notice that we have a pointer named next of the cNode type. If it were not a pointer you would end up allocating an infinite amount of memory if you were to create a variable of type cNode. Modern compilers should generate an error and not let you make that mistake. This is what makes a linked list. The current node points to the next node in the list. This is the most basic form.
Now we need to create a class that will handle managing our linked list. For this lesson you’ll learn how to create a self cleaning list. A self cleaning list is one which you don’t need to provide any means to delete the list or call any functions. It’s all handled automatically.
1 2 3 4 5 6 7 8 9 10 11 12 13 | class cNodeHandler { public: cNode * head; cNode * tail; int count; cNodeHandler() { head = 0; tail = 0; count = 0; } |
The first thing our class needs is two points of the cNode type. The head is the front of the list and the tail points to the last cNode in the list. If you don’t keep track of the front of the list or lose it you’ll end up with a memory leak since this is the only way to know where the memory is that you allocated. If you don’t know what you allocated you can’t delete it and it will remain allocated even after your program exits. The tail is used to quickly add items to the list. When this class is initialized, everything is set to 0. count is used to keep track of the number of nodes if you need to know.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ~cNodeHandler() { if(head!=0) { cNode * current=head; while(current!=0) { current=current->next; printf("%i:\t%i\tdeleted\n",head,head->x); delete head; head = current; } head = 0; tail = 0; } } |
This is the autocleaning part. You can see in the screenshot a list of numbers with “deleted.” That is the output from the destructor. A destructor is called automatically when a variable of the type is deleted from memory. So, when our cNodeHandler goes out of scope this function is called and all the nodes in the list are deleted from memory. This eliminates the need for the user of the class to supply a way to free all the memory. It’s also not necessary to explicitly call a function.
1 2 3 4 5 6 7 8 9 10 11 12 13 | void addNode(cNode &node) { if(head==0) { head = new cNode(node); tail = head; } else { tail->next = new cNode(node); tail=tail->next; } } |
To add a node we simply pass in an existing variable of type cNode and then check to see if our list has already been created. If the list is empty then head will be 0 so we set head to a new cNode. We then set the tail to the head since this is our last item. If head is not 0 then that indicates we have a tail so we set tail->next to a new node. We then move the tail pointer to our new node and we’re done. It doesn’t get any simpler than this. It’s also possible to add nodes to the front of the list in which case we don’t need a tail pointer.
1 2 3 4 5 6 7 8 9 | void toString() { cNode * current = head; while(current!=0) { printf("%i:\t%i\n",current,current->x); current=current->next; } } |
The toString method demonstrates how to parse through all the elements in the list. We basically create a temporary pointer which will keep track of where we are at in the list. We then set the current pointer to the next node until the next pointer is 0. This is why we default the next pointer to 0. If we didn’t, we’d have no way to tell when we’ve reached the end of the list and we’d get access violations.
1 2 3 4 5 6 7 8 9 10 11 | void main() { cNodeHandler nodes; cNode temp; for(int j=0;j<100;j++) { temp.x=j; nodes.addNode(temp); } nodes.toString(); } |
Our main function demonstrates how to use the handler. We add 100 nodes to the list and set x to the node number. This makes it easy to verify that everything is working properly. We then do a toString to see the contents of the list and that’s it. We see the “deleted” list in the screenshot because the destructor for cNodeHandler is automatically called when the program exits.
And that’s all there is to it. You now know how to create a simple linked list. This is something you should be able to do without having to look at a reference. Understanding how pointers work is a very important part of coding. In the next lesson we’ll show how to do a merge sort so that we can sort the list based on an element in the cNode class.
FYI: Intro to Writing “Good” Code
Posted by admin in For Your Info on May 4th, 2009
Determining whether code is good code or not is dependent on what the code is supposed to do. If your code is supposed to run fast then efficient code is good code. If the code is educational then readable code is good code. There are a number of things to consider.
- Functionality - Does the code do what it’s supposed to do
- Readability - How easy it is to read the code and follow it
- Usability - How easy it is actually use the code
- Efficiency - How well it does what it is supposed to do
- Stability - How hard it is to get the code to fail
- Reusability - How easy it is to use the code in other projects
Functionality This is the most important thing about any piece of code. It doesn’t matter how “nice” the code looks or how fast it is, if it doesn’t do what it’s suppsed to do, then it’s junk. Nonfunctioning code is most common on forums. This is because people are more interested in getting the first word in than actually being helpful. Or they simply think they know something when they actually don’t. There really is no excuse to publish nonfunctioning code anywhere. If you havn’t compiled and tested it to verify it works the way it’s supposed to, then don’t distribute it. It’s rediculous how many open source projects just flat out don’t work. After all “it’s somebody elses job to fix it.” The first rule of programming is “take responsibility for the crap you write.” If you wrote it, it’s your job to make it work.
Readability Eventually somebody (most likely yourself) is going to have to look at the code a long time after it was first written. If your intent is to publish the code for other people to use then readability is number two after functionality. If nobody can understand your code then there’s no point for it being out there. If you can’t understand your code even as little as a month after you wrote it then you’re in trouble. If you rely heavily on comments in order to understand the code then either you simply don’t understand the language well enough or the code is just plain sloppy. Comments should provide a summary of what’s going on. Not detail every little thing. “This is a for loop” or “This is x, he’s very fond of being used in for loops” are bad comments.
You should use descriptive function and variable names.
1 2 3 4 5 6 7 8 9 10 11 | //bad programmer, no pay check void ThisIsFunctionMooWhichPrintsMoo() { printf("moo\n"); } //good programmer void printMoo() { printf("moo\n"); } |
Proper whitespacing and naming makes it obvious what functions do without wasting time writing redundant comments.
For prime examples of how not to write good code, check out Magic Software. If you take a look at WmlSphere3.h, WmlSphere3.cpp you see an excessive amount of code for what amounts to a class which contains three values for the position of the sphere and the radius. Compare that to the sphere class we’ve been using. The first mistake of this code is using templates. The second is breaking the class up into two files needlessly. The class frankly does absolutely nothing but requires you pair up two files on top of a vector class which I’m afraid to look at. You’ll find a number of vector related files in the mathematics section and the code contains memcpy’s for what appears to be returning a copy of the current vector. This can also be accomplished by doing a
1 2 | class1 a,b; b=a; |
This code fails miserably on readability. The reason for this is that the author probably knows a lot about programming and has long forgotten a key rule of programming: Keep It Simple Stupid.
Usability If a piece of code requires a million things outside it’s scope to work then it’s not particularly usable. The more self contained a function is, the more useful it is. As a general rule a function should not require any external values that are not passed in as parameters.
1 2 3 4 5 6 7 | //that's it, uninstall your compiler //you will never work in this town //again int add() { return a+b; } |
Although global variables are often necessary they should be avoided where possible. The above example is completely useless in any code where a and b are not defined globaly.
1 2 3 4 5 | //that's better int add(int a, int b) { return a+b; } |
We’ve been using classes quite a bit in the tutorials. Just like functions, classes should be as self contained as possible. They should also be as single minded as possible. A vector class for instance should not contain functions that handle rendering graphics.
Looking back at the code at Magic Software, usability is also in question because of all the dependencies. We need Vector.h for the sphere class but who knows what corresponding cpp file we need to compile with it. There are a number of vector h files and vector cpp files. And even then, once you get it compiled, good luck figuring out how to use it for anything.
I’m assuming the person who has assembled this mass of code is doing it for educational purposes since there’s no indication it has a specific use for anything. This makes the choice to use templates even more questionable. If you’re writing code with the intent to teach concepts then you focus on readablity and simplicity. The less code you use, the better. The less “fancy” code you use, the better. You simply can’t write code without considering your audience. You cannot become so obsessed with C++ and Object Orientated Programming or you will lose the ability to write useful code. A “one size fits all” mentality is a very bad thing.
Efficiency For our lessons the focus is on real-time which needs efficient code but we also want to teach lessons so the code is maybe less efficient than it could be. We’ve done a number of optimizations as we increase in complexity. The idea is that we show you how it works and then show you how to make it work faster. You never want to optimize too soon. All code should be written first to work and second to work fast. If you optimize too soon the code could become more difficult to debug if problems come up later. Imagine trying to debug Wolf5K in it’s obfuscated state.
It’s easy these days to write sloppy code and not even realize it because the processors are so fast. Where possible you should develope your code on a machine that is much slower than your target system. You also need to learn what is a reasonable and not reasonable speed for something to run at. That just comes with experience. Wolfenstein 3D runs very very fast on a modern system. If you write a version and it only goes say 15 frames per second on a 1.5Ghz system then your code is obviously inefficient. Wolf3D can run twice that on a 486 machine.
You need to have a reasonable view of the complexity of your code in order to judge whether the code is inefficient or you’re just doing something very complex that a slower speed is to be expected. If you’re plotting a single pixel on the screen at one frame per second then your code is bad. If you’re plotting a billion pixels on the screen at one frame per second then your code is probably just fine. It doesn’t matter how efficient your code is, you simply can’t render Toy Story in real time.
Stability Stable code is code which doesn’t break no matter what inputs you pass into it.
1 2 3 4 | float xdivy(float x, float y) { return x/y; } |
The problem with that of course is that if y is 0 you will get a divide by zero error. To make the code stable we need to add a check to see if y is 0 and if it is, return a very large number or some other valid value. As code becomes more and more complex it becomes more and more difficult to spot conditions that will cause the program to break. As a result, code needs to be tested and retested.
Java, C++ and C# as well as other languages give you the ability to use try and catch statements.
1 2 3 4 5 6 7 8 | try { x = x / y; } catch(...) { printf("divide by zero error\n"); } |
It might be tempting to wrap your code up in those but you shouldn’t. They add overhead and are suitable for debugging and catching invalid user input only. If your code is causing an exception then you need to fix the code so it doesn’t generate the exception. Not just throw a try catch around it in an attempt to hide the problem.
1 2 | if(y!=0.0f) x=x/y; |
That is a much more solid solution. It addresses and accounts for a potential problem which results in preventing a problem instead of just addressing it after the code breaks.
Reusability After you program for awhile you realize that you are often writing the same code over and over again. Reusable code is code that you can take from one place and use in another without having to rewrite anything. If you’re doing the same operations over and over then it’s a good idea to consider writing a single function which does the operation and then call that function where needed instead of reimplementing it over and over again.
The ultimate goal of reusable code is Plug and Play classes. Plug and Play classes are classes which are entirely self contained. You simply add them to the project and you can use them. No assembly required. All batteries are included. The Core that we’ve been using in these tutorials isn’t quite plug and play. It is missing a couple functions and variables that the user must supply. The cVector class which we will cover in Lesson 29 is a plug and play class. You simply include the single h file and you have access to vectors and vector operations without having to write a single line of code.
And that concludes this side lesson. Being able to write good code is just like being able to write legibly. Some people just simply have sloppy handwriting. Some people just let their technical knowledge of a language override their common sense or simply can’t see their work from someone else’s (the audience’s) point of view. The only time it only matters that you can understand it, is if you are the only one who will ever see it.