Why version() ?
Walter Bright
newshound1 at digitalmars.com
Tue Feb 10 14:33:12 PST 2009
Steven Schveighoffer wrote:
> "Walter Bright" wrote
>> 2. version (A || B) can be done as:
>>
>> version (A) version=AorB;
>> version (B) version=AorB;
>
> And it would be much nicer NOT to have to do this.
If you find yourself doing that a lot in the code, then I suggest a
better solution is to rethink exactly which versions are being produced,
and come up with a set of version identifiers, one for each actual version.
> I think your original
> position of "let's not get into #if hell" has absolutely (and I mean
> *absolutely*) no merit as an argument against this one change.
The defense submits as Exhibit A just one snippet from Hans Boehm gc's
os_dep.c (note all it uses in #if expressions is ||):
===============================
# if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
# ifdef __STDC__
typedef void (*handler)(int);
# else
typedef void (*handler)();
# endif
# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
|| defined(HURD) || defined(NETBSD)
static struct sigaction old_segv_act;
# if defined(IRIX5) || defined(HPUX) \
|| defined(HURD) || defined(NETBSD)
static struct sigaction old_bus_act;
# endif
# else
static handler old_segv_handler, old_bus_handler;
# endif
# ifdef __STDC__
void GC_set_and_save_fault_handler(handler h)
# else
void GC_set_and_save_fault_handler(h)
handler h;
# endif
{
# if defined(SUNOS5SIGS) || defined(IRIX5) \
|| defined(OSF1) || defined(HURD) || defined(NETBSD)
struct sigaction act;
act.sa_handler = h;
# if 0 /* Was necessary for Solaris 2.3 and very temporary */
/* NetBSD bugs. */
act.sa_flags = SA_RESTART | SA_NODEFER;
# else
act.sa_flags = SA_RESTART;
# endif
(void) sigemptyset(&act.sa_mask);
# ifdef GC_IRIX_THREADS
/* Older versions have a bug related to retrieving and */
/* and setting a handler at the same time. */
(void) sigaction(SIGSEGV, 0, &old_segv_act);
(void) sigaction(SIGSEGV, &act, 0);
(void) sigaction(SIGBUS, 0, &old_bus_act);
(void) sigaction(SIGBUS, &act, 0);
# else
(void) sigaction(SIGSEGV, &act, &old_segv_act);
# if defined(IRIX5) \
|| defined(HPUX) || defined(HURD) || defined(NETBSD)
/* Under Irix 5.x or HP/UX, we may get SIGBUS. */
/* Pthreads doesn't exist under Irix 5.x, so we */
/* don't have to worry in the threads case. */
(void) sigaction(SIGBUS, &act, &old_bus_act);
# endif
# endif /* GC_IRIX_THREADS */
# else
old_segv_handler = signal(SIGSEGV, h);
# ifdef SIGBUS
old_bus_handler = signal(SIGBUS, h);
# endif
# endif
}
# endif /* NEED_FIND_LIMIT || UNIX_LIKE */
===============================
The rest of that file is ALL like that. That one little innocuous change
opens the door to hell <g>.
(Disclaimer: I do not mean to throw tomatoes specifically at Hans here,
I know Hans personally and I am in awe of his programming knowledge
skill. It's just that many developers have worked on that gc, each one
layering on another gob of conditional compilation. This result is
typical of C code that's been maintained for years, and you'll find a
similar mess in my own code. I didn't want to use my own code as the
example of hell because that is too easily dismissed as a personal
failing of mine, and that professionals wouldn't do that. But they do. C
lends itself to and encourages this kind of programming.)
>
> Think of code that is versioned around architecture that would look
> horrendous if you have to do version statements that have all different
> combinations of stuff. If I have 5 different architectures to support, I
> don't want to have to define a module that has 2^5 different version
> combinations.
Is your code really intended to actually build 32 different versions?
May I suggest instead building the 5 architecture versions, and using
runtime switches for the variants? Or perhaps abstracting the particular
features out into separate modules?
> When I add another architecture, *gasp* I have to double the statements (to
> do them now with and without version(F) ), and now I have to do another 2^5
> statements for the version(F) block. Wheee!
You're right, that is untenable. What I suggest instead is creating a
module:
module PersonalityForF;
with all the oddities for F exported as constants, aliases, types and
functions. This makes it easier for the maintenance developer who needs
to do G, he knows he's just got to write a PersonalityForG module rather
than edit the source code for dozens of other modules that have embedded
version stuff. It also eliminates the risk of breakage of those other
modules for the other platforms. (When I do a port of my projects to new
platform F, I often inadvertently break A and C because the code for A,
C and F are all mixed up together.)
I'm currently working on the Mac port, and am encountering exactly these
kinds of problems. One bright spot is I abstracted all the platform
specific library reading/writing code out into a separate file rather
than using any conditional compilation. This has worked out extremely
well. No "os_dep.c" disaster file.
>> 5. Why can't one 'version out' syntax that is not recognized by the
>> compiler?
>>
>> The problem is that supporting this requires semantic analysis in order to
>> successfully lex and parse the source code. Breaking this will make the
>> lexing and parsing an order of magnitude harder for third party tools to
>> do. If you need to 'comment out' a section of syntactically invalid code,
>> use the /+ ... +/ nesting comment.
>
> Just so you know, this is not a solution. We all know that the main reason
> people ask for this is to have code that can compile with D1 or D2 using
> versioning. /+ .. +/ doesn't help there.
I know, and I have the same issues with Phobos. See my reply to Derek.
More information about the Digitalmars-d
mailing list