11 March, 2008

Global Variables in C

First the disclaimers;
Global variables are bad. Global variables are evil. Global variables are, and will continue to be much like a 6-ft chainsaw, extremely dangerous to use, can regularly be replaced with a safer tool/technique, but are sometimes the proper tool for the job (like cutting down 12-ft diameter trees). None-the-less, on the rare occasion that a global variable is the proper technique, you'll need to know how to define and use them...the subject of the remainder of this post.

The following example will be used to explain techniques for defining and using globals, we will speak to it for the remainder of the post.

package1.h

1 #ifndef PACKAGE1_H
2 #define PACKAGE1_H
3 static const float GlobFloat = 3.14159;
4 static double GlobDouble = 1.23456;
5 extern unsigned GlobUnsigned;
6 //char GlobChar='a';
7
8 void func1(void);
9 #endif

package1.c

1 #include "package1.h"
2 #include
3
4 static int PackageGlobalInt = 1;
5 unsigned GlobUnsigned = 12345;
6
7 void func1() {
8 printf("(%s:%d) PackageGlobalInt(%d)\n",__FILE__,__LINE__,PackageGlobalInt++);
9 printf("(%s:%d) GlobFloat(%f)\n",__FILE__,__LINE__,GlobFloat);
10 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble++);
11 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned++);
12 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble++);
13 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned++);
14 }
15

package2.h

1 #ifndef PACKAGE2_H
2 #define PACKAGE2_H
3 #include "package1.h"
4
5 extern long GlobLong;
6 void func2(void);
7 #endif

package2.c

1 #include "package2.h"
2 #include
3
4 static int PackageGlobalInt = 2;
5 long GlobLong = 987654321;
6
7 void func2() {
8 printf("(%s:%d) PackageGlobalInt(%d)\n",__FILE__,__LINE__,PackageGlobalInt++);
9 printf("(%s:%d) GlobFloat(%f)\n",__FILE__,__LINE__,GlobFloat);
10 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble++);
11 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned++);
12 printf("(%s:%d) GlobLong(%li)\n",__FILE__,__LINE__,GlobLong++);
13 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble++);
14 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned++);
15 printf("(%s:%d) GlobLong(%li)\n",__FILE__,__LINE__,GlobLong++);
16 }
17

main.c

1 #include
2 #include "package1.h"
3 #include "package2.h"
4
5 int main() {
6 printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
7
8 func1();
9 func2();
10
11 printf("(%s:%d) GlobFloat(%f)\n",__FILE__,__LINE__,GlobFloat);
12 printf("(%s:%d) GlobDouble(%lf)\n",__FILE__,__LINE__,GlobDouble);
13 printf("(%s:%d) GlobUnsigned(%u)\n",__FILE__,__LINE__,GlobUnsigned);
14 printf("(%s:%d) GlobLong(%li)\n",__FILE__,__LINE__,GlobLong);
15
16
17 printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
18 }
19


Globals are a form of interface, so rightly are typically defined in a header file. Package 1 attempts to define 3 global variables. You will soon see that it was only successful in defining one. Before we get into the details, we should review some preliminaries.

#includes
As you should already be aware, header files serve little purpose other than organization of code. When encountering a #include directive, the precompiler does little more than copy-n-pasting the contents into the sourcing file. This is significant in our later discussion.

Static Variables
Static variables cannot be accessed outside a translation unit. For all intents and purposes, a translation unit is a C source file. This, along with what we know about #includes will explain why only one of our intended global variable declarations was successful.

Back To Our Example
The contents of package1.h on lines 3-5 are three attempts to declare a global variable. Note, lines 3-4 define the variables as static, line 5 defining it as external linkage. Why are the first two variables defined as static??? Because if I don't, I get a linker error "multiple definition of . . ." error. Pounding on the keyboard, slowly beating the linker into submission is accomplished by declaring the variable as static. Finally, the linker shuts up; we must have fixed it, right? Wrong!

So, why doesn't lines 3-4 work? More importantly, how do we know it doesn't work? Well, we can confirm that line 4 didn't work by comparing the results of lines package1.c:10, package1.c:12, package2.c:10, package2.c:13, and main.c:12. Note that on package 1 & 2 functions, we are incrementing the contents of GlobDouble. If we were successful in defining a global variable, we should see the variable initially assigned 1.23456, incremented 4 times with a final result of 5.23456.


~/StorageClasses$ ./main | grep GlobDouble
(package1.c:10) GlobDouble(1.234560)
(package1.c:12) GlobDouble(2.234560)
(package2.c:10) GlobDouble(1.234560)
(package2.c:13) GlobDouble(2.234560)
(main.c:12) GlobDouble(1.234560)

Hmmmph, we didn't accomplish what we intended, did we? The static storage class, coupled with the precompiler copy-n-paste behavior resulted in 3 independent variable declarations; one in package1.c, one in package2.c, and one in main.c. Each instance is unique to each translation unit, so incrementing one only affects the declaration in that translation unit.

Line 5 however successfully defined a global variable. How? Well, line 5 simply states that we are declaring a variable, to be defined elsewhere. Declaration simply defines a reference to variable that will be defined elsewhere (in this case, line 5 in package1.c). Translation units: package1.c, package2.c and main.c will each declare a reference to GlobUnsigned, but the single definition of the variable resides in package1.c.

We can confirm that all is well by means of:


~/StorageClasses$ ./main | grep GlobUnsigned
(package1.c:11) GlobUnsigned(12345)
(package1.c:13) GlobUnsigned(12346)
(package2.c:11) GlobUnsigned(12347)
(package2.c:14) GlobUnsigned(12348)
(main.c:13) GlobUnsigned(12349)



It is worth noting that lines 3-4 in package1.h have identical results as line 4 in both package1.c and package2.c. All instances are similar in the matter that their scope is limited to the translation unit in which they are declared.

10 March, 2008

Formal Names For Common C Techniques

If you ever written a C application with custom-defined header files, you've used the pre-processor pattern:


#ifndef FOO_H
#define FOO_H
.
.
.
#endif


Ever wonder what this pattern was called? I never did, until the other day when I searched for it. The pattern is referred to as the 'once-only header' pattern, sometimes called the 'wrapper #ifdef'.

Call it what you want, the intention is to ensure that the header is not copy-n-pasted multiple times in a translation unit, due to multiple direct or indirect includes. The FOO_H is referred to as the 'guard' or 'controlling macro'.

Another common technique that you may have made use of goes something like this:

#ifdef SYS1
#include "sys1.h"
#elif . . .
.
.
.
#endif

This is referred to as a 'computed includes' pattern or technique. Not much else to add, just a name for a common face.

28 January, 2008

I've Been IT'd

Once again I've been IT'd, that is "Information Technology"'d. I have recently began using it as a verb, synonymous with screwed, broken, f**cked...

Is it just me, or is it just our IT department? For the past 3 years, IT has managed to break our systems fairly regularly on a monthly schedule. The past 6 months, they've broken our network a total of 4...count 'em....four times! I'm convinced that 100 monkeys with 100 keyboards couldn't cause any more mayhem and quite possible provide better support.

While they lack competence, they thrive in lack of dedication. Come 3-4 o'clock you'd be hard pressed to fit a sheet of paper between any given meathead that makes up the steady stream of those heading for the door. Jetta after Jetta, off to screw up someone else's machine...or perhaps a marathon session of World of Warcraft (tee hee).

03 January, 2008

I See Windows FS

Ok, sure...it sounded better in my head; a pun on "I see dead people". But once down, I have to admit, it is pretty lame. Too bad! Besides, if you are a reader of this blog you're already aware that I'm humor-impaired.

Being a long-time Linux user I've encountered the daunting decision during an installation....should I make the Windows partition visible from Linux? Most of the time, the answer to this question is a 'hard' _no_. My current computer has Debian installed, and the question was again presented during the installation and I chose, once again, no. Unfortunately, I've recently found a desire to make the partition available for file transfers and manually updated my system to make the partition available. I decided to document the steps of this effort, just for fun.

- Step 1 -- determine the file system identifier for the Windows partition

$ cat /boot/grub/menu.lst
.
.
.
# This entry automatically added by the Debian installer for a non-linux OS
# on /dev/hda1
title Microsoft Windows XP Home Edition
root (hd0,0)
savedefault
makeactive
chainloader +1
.
.
.


Note: a more appropriate means to get the same information would be:

# /sbin/fdisk -l


- Step 2 -- update your fstab


$ cat /etc/fstab
# /etc/fstab: static file system information.
#
#
proc /proc proc defaults 0 0
/dev/hdb1 / ext3 defaults,errors=remount-ro 0 1
/dev/hdb5 none swap sw 0 0
/dev/hdd /media/cdrom0 udf,iso9660 user,noauto 0 0
/dev/hdc /media/cdrom1 udf,iso9660 user,noauto 0 0
/dev/scd0 /media/cdrom2 udf,iso9660 user,noauto 0 0
/dev/fd0 /media/floppy0 auto rw,user,noauto 0 0
/dev/hda1 /mnt/winXp/ ntfs uid=1000,gid=100,umask=0022 0 0


- Step 3 -- create the mount point

# mkdir /mnt/winXp


- Step 4 -- mount the file system

# mount /mnt/winXp


- Step 5 -- pat yourself on the back
(pat, pat)

06 December, 2007

OpenOffice File Converter Macro

Some time back I published an entry showing how to execute a Open Office macro from the command line. This opens the possibilities of batch processing files, the original reason I started pursuing this. Authoring a series of publications stored in odt format allows storing in native file format. Unfortunately, since this file format is far from a conventional standard this is only useful if I can convert them to a more recognized standard (like pdf).

The macro defined below accepts a file name as an argument, converts it to an associated Pdf file and exits.


REM ***** BASIC *****

Sub SaveAsPDF( cFile )

' cFile = "/home/lipeltgm/sample.odt"
cURL = ConvertToURL( cFile )
oDoc = StarDesktop.loadComponentFromURL( ConvertToUrl( cFile ), "_blank", 0, Array())
cFile = Left( cFile, Len( cFile ) - 4 ) + ".pdf"
cURL = ConvertToURL( cFile )
oDoc.storeToURL( ConvertToUrl( cFile ), Array(MakePropertyValue( "FilterName", "writer_pdf_Export" )))
Shell("pkill soffice.bin")
End Sub

Function MakePropertyValue( Optional cName As String, Optional uValue ) As com.sun.star.beans.PropertyValue
Dim oPropertyValue As New com.sun.star.beans.PropertyValue
If Not IsMissing( cName ) Then
oPropertyValue.Name = cName
EndIf
If Not IsMissing( uValue ) Then
oPropertyValue.Value = uValue
EndIf
MakePropertyValue() = oPropertyValue
End Function



Execute the macro from the command line as follows:


$ soffice -invisible "macro:///Standard.Module1.SaveAsPDF(/home/myUser/sample.odt)"

It's worth noting that the 3rd '/' in 'macro:///' implies the root directory of the application, in this instance the root directory.

02 December, 2007

OpenOffice Command Line Macro Execution with Args

soffice "macro:///Standard.Module1.SaveAsPDF(/home/lipeltgm/sample.odt)"

macro:///
third / implies root directory of application

Command Line OpenOffice Macro Execution

First start by creating a new macro, something simple.

Create the macro by selecting Tools->Macros->Organize Macros->OpenOffice.org Basic

Edit a new Macro1 in Module1










Define the Macro1 as below:

Sub Macro1
MsgBox( "Hello World" )
End Sub


Save it and execute it from the command line by:

$ soffice macro:///Standard.Module1.Macro1

You can run OpenOffice headless by specifying the -invisible argument.

$ soffice -invisible macro:///Standard.Module1.Macro1