From: mjr@TIS.COM (Marcus J. Ranum)
Newsgroups: rec.games.mud.misc
Subject: Re: Note to any fellow DikuMUD coders...
Date: 26 Dec 92 22:23:08 GMT
Reply-To: mjr@TIS.COM
Organization: Trusted Informations Systems, Inc.

arromdee@jyusenkyou.cs.jhu.edu (Ken Arromdee) writes:
>>#ifdef	BROKEN_INET_NTOA
>>#ifdef	SUN
>>#else
>>#endif
>>#else
>>#endif
>>	And similar abominations.
>
>Yet, what is the alternative?  The alternative seems to be to not take into
>account broken software at all, and to always write as though it worked even if
>it doesn't.  This creates an _immediate_ "software time bomb".

	The least unpleasant alternative (I don't think ANY of this stuff is
exactly pleasant) is to write modular code and provide library functions
that can be specified on a per-system basis. A good example of this is
how large, well-engineered, commercial products (such as Oracle) achieve
portability. Cnews also does this with a pretty high degree of success
and a low #ifdef count. See Henry Spencer's '#ifdef considered harmful'
in the San Antonio USENIX talks for an interesting discussion of the topic.

	What you basically need to do is write a module that you can
"library-ize" on a per-system basis. Suppose you need to do low granularity
file locking in a program. This, traditionally, is subtly broken between
various forms of UNIX and network filesystems. You write a lock_file()
routine and an unlock_file() routine and provide a simple interface to
those. Then, your software gets configured at *link* time rather than
at compile time, by linking against a version of the routine that is
known to work for the particular system it is being built on. An example
of doing this would be to assume that the system's inet_ntoa() works[1]
but to provide a library with inet_ntoa() implemented using sprintf()
that could be linked in on specified systems. That way you can reconfigure
the program by changing the makefile, not the *code*. For large
applications this is absolutely the only way to do things. In a
large-scale system it's actually important to keep the *names* of
functional units the same for debugging purposes. At least if you
can assume you have a "lock_file()" function, then you know you
can set a breakpoint there. If you have a file containing a wad
of #ifdefs then you're on your own. This is not a concern for
small programs, but if you write small programs with the care and
attention required for large ones, you'll find your small programs
tend to work a whole lot better than stuff written by folks who
just hammer away in a text editor.

	With respect to Ken's remarks about "not everyone is a programmer",
I couldn't agree more. Whenever you're developing software you have to
determine what your target audience is and program accordingly. That's
why a lot of folks go to extreme lengths to write Makefiles and programs
that are "smart" and autoconfigure themselves (or try real hard and
usually fail each time there's a new operating system release). That's
because they're catering to the non-programmer. Unless I'm being paid to
cater to non-programmers (I.e.: I'm *working* instead of *playing*) then
my obligation to them is appropriately reduced. Before you jump up and
down about how it's impossible to design library-configuring code that
is nontrivial but that almost any idiot can install, take a look at
Larry Wall's stuff, or the Cnews distribution. They're amazing pieces
of work.

mjr.

[1] I tested inet_ntoa() on our Solobourne and a Sun running 4.1 and it
seems to work fine. This is another problem with kludging up your
code because you think vendor code is broken. Sometimes it gets
fixed and sometimes it works fine all along.
Newsgroups: rec.games.mud.misc