Tuesday, November 11, 2008

Digital Peer

Found a really cool and useful site while browsing around
http://www.digitalpeer.com/

There are tips, tutorials and articles here.

Hope it stays online!

Saturday, July 26, 2008

On Reviews

Reviews are like makeup; they can't make you beautiful, only less ugly.
- TJ

Monday, January 28, 2008

Perly Gates

Tip 1:
Replacing string content with another string when both could contain backslashes.


Explanation:
Basically, you have to use \Q to escape. Otherwise, the contents of the strings (search & replace strings) will be interpreted as escape sequences.

Example:
The following is an extract from a script to get the relative path of a file with respect to a base-directory.

#!/usr/bin/perl

$BASE = "d:\source\";
print "
Base : $BASE\n";

$TARGET = "
d:\source\temp\test.txt";
print "
Original : $TARGET\n";


$TARGET =~ s/\Q$BASE//;
#Replacing with null string
#syntax for replace:
#TargetString =~ is s/SearchString/ReplaceString/

print "
Modified : $TARGET\n";
#Target becomes "
temp\test.txt"


Tip 2:
Replace backslashes with forward slash.

If you do this the regular way, the regex you write will likely look like a squiggly drawing :) Something like /\/\\/ ;), which is a pain to read or debug.

#!/usr/bin/perl
# In Perl, any character can be used to delimit the regex! Here, using @ as separator
$STRING =~ s@\\@/@g;


Yeah, it's still ugly and we need to escape the backslash as \\, but that's they way it goes. Use this tip whenever you need to replace

Tip 3:
Get command line output into a variable


Here's a function to do this. Takes two parameters, the command-string to execute and a boolean option for showing the output on the display.

sub getCommandOutput
{

my ($COMMAND, $DISPLAYOUTPUT) = @_;

open(COMMAND_OUTPUT, "$COMMAND 2 >&1 |");
my @OUTPUTLINES = <COMMAND_OUTPUT>;
chomp(@OUTPUTLINES);
close(COMMAND_OUTPUT);

foreach $OUTPUTLINE(@OUTPUTLINES){

print "\n$OUTPUTLINE" if $DISPLAYOUTPUT;
}

print("\n") if ($DISPLAYOUTPUT);
return @OUTPUTLINES;

}


# TODO You may want to capture the error stream separately

The function returns the output of the command as an array which you can store into a variable.




Thursday, December 13, 2007

Commentstipation

When it comes to commenting the code, the cat gets the typing fingers of even creative and knowledgeable programmers. Often we are focused on just getting the code working (We'll comment later! The code has to work first!) and then we forget (what was that again? it's obvious, also time to go home...)!

Code should be self-explanatory and hence self-commenting, as far as possible. But this definitely does not mean that there should be no comments.

Good comments are hard to come by because of -
a) I-Am-A-Programmer-Not-A-Writer attitude
Well, you are a writer too.

b) "I don't know English that well" excuse
Grammatical errors are fine, spelling mistakes are also OK. Write in your native tongue and translate it, if need be. And, if what the code does is that tough to explain, then it's also the more reason to document it, it probably needs to be documented!

Remember the programming adage: Documentation is like sex, something is better than nothing! ;-) But, it must be said, inaccurate documentation is worse than no documentation. An addition to the adage is perhaps needed...having it the wrong way is probably worse than not having it at all! ;-)

TIPS
--------
* A comment should, at the very least, explain and focus on what is special in the block of code. A very good comment also explain why it is being done.

* The best comments explain the what and the why succinctly.

* How to comment
1. Ask yourself "What?"
2. Ask yourself "Why?"
3. Write what you would say to someone so that he could do what you have done. Use simple language. (This point intentionally abstruse, to serve as a memory-aid)
4. Review and edit what you have written, the best you can.

* Do not state the obvious! But also remember that what is obvious to you may not be obvious to another person. It's a thin line between advising and preaching.
Code like:
x=0; // Assigning the value 0 to the variable x
is a big no-no. Do not insult the intelligence of the reader. Plus, you'll have to scroll more while editing the code!

Monday, December 10, 2007

Coding Nirvana - Cryptic Mystic Droppings

CRYPTIC MYSTIC DROPPINGS
FROM THE ELEVATED STATE
OF
CODING NIRVANA

1. Code is just symbols.

2. Code needs a Thread of Life to achieve its Purpose.

3. It's all about the data.

4. It's all virtual. It can be faked but it doesn't really matter; it isn't really matter anyway.

More on each of these droppings (shit!) later.

Saturday, December 08, 2007

Coding Nirvana - Four Noble Truths

CRYPTIC MYSTIC DROPPINGS
FROM THE ELEVATED STATE
OF
CODING NIRVANA

-----------------------------------------------------------
FOUR NOBLE TRUTHS
-----------------------------------------------------------
1. There is suffering
2. There is a cause of suffering. The cause is "Copy-paste"
3. There is the cessation of suffering - "Abstraction Of Commonality"
4. There is a way leading to the cessation of suffering — the Noble Eightfold Path

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

"Copy-paste is the root cause of all programming suffering. Copy-paste is evil. The Eternal Conflict is between copy-paste and the Abstraction of Commonality."
- The Virtual Mystic

Sunday, November 25, 2007

C Powershot - Pointers

INTRO
---------
How should one interpret the following lines of C?

int *p;
"easy! p is an integer pointer!"

int **p;
"p is a pointer to an integer pointer" or perhaps you might say "p is a double pointer to an integer"

int ***p;
"hmm...er..ahem..why would anybody use such a thing! *@#$ ?"


POWERSHOTS - Interpreting a pointer declaration
----------------------------------------------------------------
The interpretations given i n the preceding section, even if somewhat correct, do not scale and could inhibit our ability to understand alien (written by other people) code. The words influence the way we think, so it's necessary that we choose the right abstractions. For example, if a pointer is 4 bytes, why shouldn't a double pointer be 8 bytes? :-) The right abstraction would not even allow us to stray down such lines of thought!

Here's a better way to interpret pointers.

SNN1.1 Pointers are variables which can hold the address of a memory location, usually the address of a variable.

Pointer Part
Consider the statement
int *p;
What the "*p;" portion of the statement says is only this : p is a pointer variable

Let's call the "* p" portion here the pointer part of the pointer declaration. It tells us this much p is a pointer and * operation can be applied on it.

CPS1.1 Whatever follows the * symbol is the pointer variable.

SNN1.2 Pointer variables in C have the * operation (fancier name: dereference) defined on them.

The deference operation gets the contents of the memory location held in the pointer. That is, if you dereference a pointer, you get what the pointer points to.

****

Type Part
Consider, again, the statement:
int *p;
What the "int " part means is this: when you apply the * operator on p, what you get will be interpreted as an integer. It can be used as an integer.

Let's call the "int " portion here the type part of the pointer declaration.

CPS1.2 Whatever remains in the statement after you blank out the pointer portion will be the type of what you get when you dereference the pointer.

FINGER-HIDING TECHNIQUE: Just hide the *p section with your finger, what remains is the type part. This finger-hiding technique can come in handy in other situations as well. It's nifty and mighty useful. It is an application of what I call the typedef principle, we will come to that in a later episode.

Summary:
Pointer declaration = pointer part + type part.

To reiterate, int *p means: p is a pointer, which will be dereferenced as "int"

EXAMPLES
---------------

EX1
int **p;
Pointer part: *p ====> p is a pointer
Type part: int * ====> when you dereference p, what you get should be interpreted as"int *".
You already know what int * means according to the power-shots! This has to be done repeatedly.

EX2
int *p[6][6];
Pointer part: *p =====> p is a pointer
Type part: int __ [6][6] ====> when you dereference p, what you get should be interpreted as "int [6][6]".
This is an array of integers with 6 rows and 6 columns. int

EX3
int (*p)(int i, int j);
Pointer part: *p =====> p is a pointer. The parentheses are required because otherwise due to precedence rule, the * would be associated with int and not p.
Type part: int __ (int i, int j) ==>when you dereference p, the type of data you'll get is "int (int i, int j) ".
This is an integer function which takes two parameters.
Yup, p is a function pointer. (But you do know better now, right? p is just a pointer, when you dereference it you will get something that can be used as a function)

EX4
int (*p(int a)) (int *b);
Pointer-part: *p ====> p is a pointer
Type-part: ( __ (int a)) ==> *p is a function.
So, p is a pointer to a function.
The remaining part is the type of the function.
Finally, p is a pointer to a function, which takes an integer, and returns a function which takes an int* parameter and returns an int!

Aside: Actually, the type part of the declaration is what is within the parentheses enclosing the pointer-part, but that would have confused you; also, this is not needed in the vast majority of cases. Parentheses always rule and dictate, as you should have guessed from the previous example as well!

You'd be much better off using typedefs for complex declarations like this one. But that does not mean that one should not know how exactly it is being interpreted. :-) More on typedefs later. For the time being, referring you to http://www.gotw.ca/gotw/046.htm where this particular example was taken from.

POSSIBLE GOTCHAS
-----------------------------------------
1. Function-pointers can, on some architectures, require more space than normal pointers. If code memory uses a different addressing size/scheme, for instance. Have not encountered this though.
2. Please use parentheses liberally(but judiciously!) inside declarations and the * operator while dereferencing the pointer. These are often skipped and lead to confusing (nah, 'misinterpretable') code.

Wednesday, October 03, 2007

Looking For A Function

I am looking for a function
y = f(x1,x2...,xn)
such that given y and n, it would be possible to uniquely determine each of the x-factors.

1) It's OK for n to have an upper bound k, if k>=10 or so.
2) It is also essential that y be small

Any pointers would be welcome.

Wednesday, September 26, 2007

Code Is Prose



Coding is a form of expression. We can draw many parallels between coding and writing; we write code, we are the authors of the code. When we code, we are actually translating our ideas and understandings into the the programming language. By that line of reasoning, a program is a user manual (or essay or poem, take your pick) that we write in a programming language.

Code is for reading
(and execution too)
-----------------------------
The computer doesn't care how we write the code as long as it works - no indentation is fine, cryptic lines are fine. We should try to get as close to natural language as possible. Do not go overboard though! Code like checkWhetherTheQueueIsEmpty() are no-no's though! Advise, but do not condescend.

Code is read many more times than it is written; code is WORM (Write Once Read Many). Debugging will be done on the code that you write more times than you write actual functionality into it. The code-maintainer will curse you less (he will curse anyway!) if the code you wrote, even if it is wrong, is easily understood. So code with meaning and gain some good karma!

But the clincher argument would be that we would have less of those pesky comments to write!

"Programs must be written for people to read, and only incidentally for machines to execute."
- Abelson & Sussman, SICP

N COMMANDMENTS
----------------------------
* Avoid meaningless names.

* The meaning should not be ambiguous.

* Do not ever use a name that will not occur naturally to a person debugging the code.

* Do not abbreviate unnecessarily. Even if the abbreviation is logical to you, it might not be to another person.

* Vowel-swallowing is not desirable, nt_dsrbl at all.

* Be consistent. If you use underscores in your names to separate words, please don't use camel-case elsewhere and vice versa.
a) Capitalization -
b) Abbreviations - If you have to abbreviate (more often than not, this is a case of "I like to, hence I will") , then at least abbreviate consistently.


Recommended reading
-----------------------------
Literate programming
Writing Unmaintainable code (original)
Writing Unmaintainable code(expanded)

Wednesday, August 29, 2007

Saved By A Unit Test

Consider a function called openCDTray( ) which ejects a CD from the drive.

This function should be operated only when the CD tray is closed. The function also has the following constraint(for effect). Attempting to open the tray when it is already open could result in the tray falling off and reattaching the tray is a cumbersome activity! :-)

The system maintains the status of the tray in a global variable/object called gCDStatus. openCDTray( ) should check the status and then only attempt to eject; otherwise all hell would break loose. But does the function implementation take care of this? Maybe the developer thought that nobody in their right mind would do such a thing and omitted the check. It's so obvious!

A unit test-case to check the response of the function in such a scenario could simply do the following:
1. Set gCDStatus to TRAY_IS_OPEN.
2. Call the function.
3. Check the result. The function should not have succeeded.

But our developers would never skip such basic checks! But even in such cases, unit-test can catch errors that could be missed in a cursory inspection of the code.
1. Typos in the assertion-check.
if ( gCDStatus = TRAY_IS_OPEN) throw ExceptionAlreadyOpen;
or if you consider that to be improbable too
if( gCDStatus = CD_EJECTED) throw ExceptionAlreadyOpen
, where CD_EJECTED is a similar-looking, but different valid value for gCDStatus

2. The openCDTray( ) function might have been modified (copy-paste!) and the programmer inadvertently does something that causes a change of the gCDStatus value.

The unit-test also helps to find out whether the assertion-check has in fact been skipped. This becomes crucial during integration and has to be guaranteed before functional testing starts.
1. The user of the function may not be aware of all the preconditions and hence may not ensure all of them before calling the function.
2. The definition of another part of the system may have changed.

In the example of openCDTray( ) , we would be saved a trip to the CD repair shop by the unit-test!

Sunday, August 19, 2007

Unit Test Scripts

A unit-test function correspoding to each unit-test case broadly consists of the following sections.

1) Precondition Tweaking (Prologue)
Prepare the conditions necessary for the function to execute. Do this for all preconditions not mandated by the test-case; not supplying precondition(s) to see if the function fails may be the test-case.

2) Invocation (Test)
Call the function.

3) Post-condition Verification (Epilogue)
Check whether all the post-conditions have been enforced by the function. Check whether the result tallies with the expected result of the unit-test case.

4) Logging
This may be part of the unit-testing framework itself, if you are using one.

Link the test-function with the test-harness and the unit to be tested. Call these test-functions from a driver program to see how your unit copes!

Friday, August 17, 2007

Unit Test Environment

A Unit Test Suite consists of code which verifies and validates the unit within the unit-testing environment. It consists of -
1) Test Script - Code which calls different functions of the unit under different conditions.
2) Test Harness - In order to achieve its functionality, the unit under test might need the help of other modules. Substitute all such external functions with dummy versions, stripped to the bare minimum.

The unit under test should be run under this unit-testing environment. For the unit of a complex, heterogeneous system, it is more practical and useful to have a unit-testing environment which is much simpler than the actual deployment environment.

Aside: It is tempting to make the unit-test environment "more real, just to see if it works too; anyway I am testing, so why not?". So if you do this, you will end up doing something that's neither unit- nor functional-testing, and you won't get the possible benefits of either!

More about test-scripts and test-harnesses later.