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