///////////////////////////////////////////////////////////////////////////
// 07. C and The Portability Issue: #ifdef, or Interface Layers? //
// By: Brent York //
// AKA ThaDragon @EFnet's #c //
// Email: york@nbnet.nb.ca //
// Nuclear Winter Entertainment //
// Professional Coder for Spheringer Technologies Inc. //
///////////////////////////////////////////////////////////////////////////
==============================================================================
First off, Id like to start with a plug for gods own editor, vim. We all
know the real reason he couldnt work on sunday was he tried emacs, and
the bloat got to him ;}.
==============================================================================
Secondly, Id like to state that the views in here are my own, not Nuclear
Winter Entertainment's, or Spheringer Technologies'.
==============================================================================
Thirdly, Im not always the best at putting my thoughts into words, so
forgive me if this comes out totally wrong ;}
==============================================================================
C and C++ are beautiful languages. Thier widespread use has made
portability an issue. Here we look at ways of keeping the code portable yet
readable. You will find this document baised towards interface layers,
due to the fact that they are much cleaner.
I will outline both ways, define thier needs, and thier pros and
cons in this article, and hopefully by the end youll step back and take a
look at portability from a totally different angle.
But before I talk about both ways, perhaps its best to cover portability
in general. For those of you who dont know what portability is, its the
codes ability to compile (and function) properly on another platform.
If you stick to ANSI C then your code should be one hundred percent portable.
Unfortunately sticking to the ANSI C standard doesn't always have all the
options you need, and you dont always want to rewrite functions that may
be in your library.
Portability is a good thing and a must for most programs.
This is why you constantly see programs that compile for multiple
platforms. If you examine the source closer, most of the time you will
find that the source is rittled with #ifdef/#endif directives for conditional
compilation. This is because platforms usually go about doing the same
task differently. You have to expect that, infact it should be pretty
obvious that UNIX(R) isn't going to act exactly the same as Linux,
FreeBSD, SunOS, Solaris, or other unix based OS, much less dos, or windows.
For those of you who dont know, #ifdef, #endif are conditional
compilation directives. They are of the family, #ifdef, #ifndef, #if, #else,
#elseif, #define, and #endif. When used with "portability" in mind, you
would encase different pieces of code. This is sufficient to allow a
conditional compilation to produce the right code for the right platform,
and the resulting executable will be for that platform.
Examples:
#define __blah__
#ifdef __blah__
printf("Blah is defined\n");
#endif
#ifndef __blah__
printf("Blah is not defined\n");
#endif
This of course prints "Blah is defined".
So you ask, what is the big deal with #ifdef/#endif blocks?
Nothing, IF they are used properly. However you at some point might want
to use conditional compilation within a "while loop" for example. Now, we
all know that in loops we indent, its common structure theory. However,
do we indent the #ifdef/#endif? Well we can, but youll notice one thing
quickly. Indented, or not, its harder to read, and harder to comprehend
at a glance.
This is one of the many cons of conditional compilation
portability. Others are more #defines, more compiler switches on the
command line (an example would be gcc 's -D's for defines).
Pros for conditional compilation portability certainly do not outway
the cons. Some pros for the conditional compilation however include the
ability to have all the source for certain functions being in one file
for easy accessability, another being that the sources makefile would be
much less cryptic. And we all know that configuration programs can easily
be written in portable ansi C, or even a bash script (if for unix), which
could take care of the #defines in a config.h file.
So wheres the problem you ask? Well as I stated, readability and
reuseability is king, and unfortunately conditional compilation
portability just doesn't seem to support that concept.
So you ask for an example of conditional compilation for portability?
Heres one to port between dos and unix with compilers that use getch() and
getchar():
#ifdef __DOS__
#include
#define getchar getch
#endif
This of course would be embedded in more code. You can see the
possibilities of it ruining readability from there.
However, there is a second way to make something portable. Scince
to do anything "useful" in many cases, one must use compiler or platform
specific code in some places, one could simply write an interface "library"
for each of the platforms, and leave the conditional compilation to the
makefile(s).
This way is to make interface layers. By interface layers I mean
versions of libraries for each platform, each with the same API, and each API
call preforming the same task on thier respective platform as the interface
layers for each other platform would.
Now, that explanation was horrid, so, Ill defer you to Mage, my
graphics engine. I currently have coded it to be portable between dos, linux
svgalib, and X11 with MITSHM. Now, note, all those platforms are QUITE
different, and if my code that uses these "interfaces" must preform the
same task on each, then each of these interfaces must have a standard set
of functions (standard to all of the interfaces), and they must preform
the code necessary to do the task on THIER PLATFORM ONLY.
This means that solong as I have an interface layer for the platform
Im compiling for, my code written for those interfaces will compile
without a hitch, and without any changes. Most importantly it will not
be studded with #ifdef's and #endifs. This means that readability will
not be sacrificed, which is a good thing.
So thats one pro, what are some others?
Compilation takes less time and possibly less resources due to only having to
compile code, not make choices on what code to compile in a piece of source.
You wont need that ansi C configuration for conditional compilation defines
anymore.
Code is easier to modify and test for a specific platform, compile what
you want, test what you want, dont worry about defines.
Code may possibly run faster than if ifdef'ed. Note that some may argue
this point, but its valid. This is because the interface layer is a
"carbon copy" of the code from other layers differing only where it needs
to to get the job done correctly, therefore its only going to be slower
if you have to call functions for other libraries to do the job. An
example of this would be svgalib.
There are quite a few more, but I think its obvious by now that this
is a good choice if you want portability. Also worthy of note is that
interface layers are a form of abstraction.
Cons?
Well, theres more source, which means a source packet would be larger.
Your source wont be all in the same file for the same functions anymore,
they will be in specific code based libraries for each platform. However,
this can easily be argued as a pro due the the fact that it makes code easier
to manage.
Your makefile is larger, and possibly more complex. But it can be split
into many makefiles, each for each interface library.
So the concept here is that possibly the interface suggestion for
portability is a good thing. In a world where disk space is cheap, whats
a few more bytes for the ability to read and modify your source, and
track down bugs faster?.
Another pro for the interface layers is the fact that it makes
you adhere to the same standard for each interface of the same type for
each platform. For example, if Im writing a dos mouse interface, I might
have the function getstatus. Under linux svgalib I would have that same
name for the function that does the same thing under linux svgalib. I
would also use the same name for the function that does the same task in
X. Thusly, in doing so, my code can compile and run the same on any
platform the code interfaces support.
So I think by the end of the article you can prettymuch tell that
Im all for this. The question is have I sold you on it? I certainly hope
I have, because it seems like the better more structured way to do
things. And the last thing we need in a world where everyone shares code,
is less structure, and more stress.
Thank you for your time, I hope i've at least given you all something to
think about.
ACKS: Doc_O for proofreading and suggesting fix ups on the original
draft.
C Scene Official Web Site :
http://cscene.oftheinter.net
C Scene Un-Official Email :
cscene@mindless.com
This page is Copyright © 1997 By C Scene. All Rights Reserved