Why version() ?

Nick Sabalausky a at a.a
Tue Feb 10 16:39:16 PST 2009


"Walter Bright" <newshound1 at digitalmars.com> wrote in message 
news:gmsvb8$e1p$1 at digitalmars.com...
> Steven Schveighoffer wrote:
>> 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 ||):
>
> ===============================
{horrid mess of C snipped}
> ===============================
>
> The rest of that file is ALL like that. That one little innocuous change 
> opens the door to hell <g>.
>

Ok, but now with D-style version(), that horrid mess becomes this horrid 
mess:

======================
version(SUNOS5SIGS)
{
    version=SUNOS5SIGSorIRIX5orOSF1orHURDorNETBSD;
    version=IRIX5orHPUXorHURDorNETBSD;
}
version(IRIX5)
{
    version=SUNOS5SIGSorIRIX5orOSF1orHURDorNETBSD;
    version=IRIX5orHPUXorHURDorNETBSD;
}
version(OSF1)
{
    version=SUNOS5SIGSorIRIX5orOSF1orHURDorNETBSD;
}
version(HURD)
{
    version=SUNOS5SIGSorIRIX5orOSF1orHURDorNETBSD;
    version=IRIX5orHPUXorHURDorNETBSD;
}
version(NETBSD)
{
    version=SUNOS5SIGSorIRIX5orOSF1orHURDorNETBSD;
    version=IRIX5orHPUXorHURDorNETBSD;
}
version(HPUX)
{
    version=IRIX5orHPUXorHURDorNETBSD;
}
version(NEED_FIND_LIMIT)
{
    version=NEED_FIND_LIMITorUNIX_LIKE;
}
version(UNIX_LIKE)
{
    version=NEED_FIND_LIMITorUNIX_LIKE;
}

version(NEED_FIND_LIMITorUNIX_LIKE)
{
    version(__STDC__)
        typedef void (*handler)(int);
    else
        typedef void (*handler)();

    version(SUNOS5SIGSorIRIX5orOSF1orHURDorNETBSD)
    {
        static struct sigaction old_segv_act;
        version(IRIX5orHPUXorHURDorNETBSD)
            static struct sigaction old_bus_act;
    }
    else
        static handler old_segv_handler, old_bus_handler;

    version(__STDC__) // This would need an additional workaround due to 
syntax issues
    {
        void GC_set_and_save_fault_handler(handler h)
        {
            version(SUNOS5SIGSorIRIX5orOSF1orHURDorNETBSD)
            {
                struct sigaction      act;
                act.sa_handler        = h;

                version(none) /* Was necessary for Solaris 2.3 and very 
temporary      */
                              /* NetBSD bugs. 
*/
                    act.sa_flags          = SA_RESTART | SA_NODEFER;
                else
                    act.sa_flags          = SA_RESTART;

                (void) sigemptyset(&act.sa_mask);
                version(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);
                    version(IRIX5orHPUXorHURDorNETBSD)
                    {
                        /* 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);
                    }
                }
            }
            else
            {
                old_segv_handler = signal(SIGSEGV, h);
                version(SIGBUS)
                    old_bus_handler = signal(SIGBUS, h);
            }
        }
    }
    else
    {
        void GC_set_and_save_fault_handler(h)
        handler h;
        {
            version(SUNOS5SIGSorIRIX5orOSF1orHURDorNETBSD)
            {
                struct sigaction      act;
                act.sa_handler        = h;

                version(none) /* Was necessary for Solaris 2.3 and very 
temporary      */
                              /* NetBSD bugs. 
*/
                    act.sa_flags          = SA_RESTART | SA_NODEFER;
                else
                    act.sa_flags          = SA_RESTART;

                (void) sigemptyset(&act.sa_mask);
                version(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);
                    version(IRIX5orHPUXorHURDorNETBSD)
                    {
                        /* 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);
                    }
                }
            }
            else
            {
                old_segv_handler = signal(SIGSEGV, h);
                version(SIGBUS)
                    old_bus_handler = signal(SIGBUS, h);
            }
        }
    }
}
======================

Ok, look, we're still knee-deep in hell!

So now you say, "But wait! In D-style, you should change all that to this:"

======================
module MyGC;

version(SUNOS5SIGS) version=SunnyishOS;
version(IRIX5)      version=SunnyishOS;
version(HURD)       version=SunnyishOS;
version(NETBSD)     version=SunnyishOS;
version(NEED_FIND_LIMIT) version=SomethingMeaningful;
version(UNIX_LIKE)       version=SomethingMeaningful;

version(SomethingMeaningful)
{
    version(__STDC__)
        typedef void (*handler)(int);
    else
        typedef void (*handler)();

    // Alternatively, we could "invert" these,
    // ie, #include all of them, and have those
    // headers take care of their own #ifdef stuff.
    // Downside is: all platforms would need all sources.
    version(SunnyishOS)
    {
        version(__STDC__)
        {
            version(GC_IRIX_THREADS)
                public import MyGC_SunnyishOS_STDC_IrixThreads;
            else
                public import MyGC_SunnyishOS_STDC_NonIrixThreads;
        }
        else
        {
            version(GC_IRIX_THREADS)
                public import MyGC_SunnyishOS_KR_IrixThreads;
            else
                public import MyGC_SunnyishOS_KR_NonIrixThreads;
        }
    }
    else version(OSF1)
    {
        version(__STDC__)
        {
            version(GC_IRIX_THREADS)
                public import MyGC_OSF1_STDC_IrixThreads;
            else
                public import MyGC_OSF1_STDC_NonIrixThreads;
        }
        else
        {
            version(GC_IRIX_THREADS)
                public import MyGC_OSF1_KR_IrixThreads;
            else
                public import MyGC_OSF1_KR_NonIrixThreads;
        }
    }
    //etc...
    else
    {
        version(__STDC__)
            public import MyGC_MISC_STDC;
        else
            public import MyGC_MISC_KR;
    }
}
-----
module MyGC_SunnyishOS_STDC_IrixThreads;

static struct sigaction old_segv_act;
static struct sigaction old_bus_act;

void GC_set_and_save_fault_handler(handler h)
{
    struct sigaction      act;
    act.sa_handler        = h;

    /* Was necessary for Solaris 2.3 and very temporary      */
    /* NetBSD bugs.                                          */
    //act.sa_flags          = SA_RESTART | SA_NODEFER;
    act.sa_flags          = SA_RESTART;

    (void) sigemptyset(&act.sa_mask);

    /* 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);
}
-----
module MyGC_SunnyishOS_KR_NonIrixThreads;

static struct sigaction old_segv_act;
static struct sigaction old_bus_act;

void GC_set_and_save_fault_handler(h)
handler h;
{
    struct sigaction      act;
    act.sa_handler        = h;

    /* Was necessary for Solaris 2.3 and very temporary      */
    /* NetBSD bugs.                                          */
    //act.sa_flags          = SA_RESTART | SA_NODEFER;
    act.sa_flags          = SA_RESTART;

    (void) sigemptyset(&act.sa_mask);
    (void) sigaction(SIGSEGV, &act, &old_segv_act);

    /* 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);
}
-----
module MyGC_MISC_STDC;

static handler old_segv_handler, old_bus_handler;

void GC_set_and_save_fault_handler(handler h)
{
    old_segv_handler = signal(SIGSEGV, h);
    version(SIGBUS)
        old_bus_handler = signal(SIGBUS, h);
}
======================

To which I reply, "Yes, and in C-style you should do exactly the same:"

======================
// MyGC.h;
#if defined(SUNOS5SIGS) || defined(IRIX5) || defined(HURD) || 
defined(NETBSD)
#    define SUNNYISHOS
#endif

#if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
#    define SOMETHING_MEANINGFUL
#endif

#ifdef SOMETHING_MEANINGFUL
# ifdef __STDC__
        typedef void (*handler)(int);
# else
        typedef void (*handler)();
#   endif

// Alternatively, we could "invert" these,
// ie, #include all of them, and have those
// headers take care of their own #ifdef stuff.
// Downside is: all platforms would need all sources.
# ifdef SUNNYISHOS
#  ifdef __STDC__
#   ifdef GC_IRIX_THREADS
#      include "MyGC_SunnyishOS_STDC_IrixThreads.h"
#   else
#      include "MyGC_SunnyishOS_STDC_NonIrixThreads.h"
#   endif
#  else  /*__STDC__*/
#   ifdef GC_IRIX_THREADS
#      include "MyGC_SunnyishOS_KR_IrixThreads.h"
#   else
#      include "MyGC_SunnyishOS_KR_NonIrixThreads.h"
#   endif
#  endif /*__STDC__*/
# else  /*SUNNYISHOS*/
#  ifdef OSF1
#   ifdef __STDC__
#    ifdef GC_IRIX_THREADS
#        include "MyGC_OSF1_STDC_IrixThreads.h"
#    else
#        include "MyGC_OSF1_STDC_NonIrixThreads.h"
#    endif
#   else  /*__STDC__*/
#    ifdef GC_IRIX_THREADS
#        include "MyGC_OSF1_KR_IrixThreads.h"
#    else
#        include "MyGC_OSF1_KR_NonIrixThreads.h"
#    endif
#   endif /*__STDC__*/
#  else  /*OSF1*/
#   ifdef(__STDC__)
#       include "MyGC_MISC_STDC.h"
#   else
#       include "MyGC_MISC_KR.h"
#   endif
#  endif /*OSF1*/
# endif /*SUNNYISHOS*/
#endif /*SOMETHING_MEANINGFUL*/
-----
// MyGC_SunnyishOS_STDC_IrixThreads.h;
static struct sigaction old_segv_act;
static struct sigaction old_bus_act;

void GC_set_and_save_fault_handler(handler h)
{
    struct sigaction      act;
    act.sa_handler        = h;

    /* Was necessary for Solaris 2.3 and very temporary      */
    /* NetBSD bugs.                                          */
    //act.sa_flags          = SA_RESTART | SA_NODEFER;
    act.sa_flags          = SA_RESTART;

    (void) sigemptyset(&act.sa_mask);

    /* 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);
}
-----
// MyGC_SunnyishOS_KR_NonIrixThreads.h
static struct sigaction old_segv_act;
static struct sigaction old_bus_act;

void GC_set_and_save_fault_handler(h)
handler h;
{
    struct sigaction      act;
    act.sa_handler        = h;

    /* Was necessary for Solaris 2.3 and very temporary      */
    /* NetBSD bugs.                                          */
    //act.sa_flags          = SA_RESTART | SA_NODEFER;
    act.sa_flags          = SA_RESTART;

    (void) sigemptyset(&act.sa_mask);
    (void) sigaction(SIGSEGV, &act, &old_segv_act);

    /* 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);
}
-----
// MyGC_MISC_STDC.h
static handler old_segv_handler, old_bus_handler;

void GC_set_and_save_fault_handler(handler h)
{
    old_segv_handler = signal(SIGSEGV, h);
    version(SIGBUS)
        old_bus_handler = signal(SIGBUS, h);
}
======================

And for the record, with the suggested improvements to D's version(), the 
"good" D-style can change from this:

======================
module MyGC;

version(SUNOS5SIGS) version=SunnyishOS;
version(IRIX5)      version=SunnyishOS;
version(HURD)       version=SunnyishOS;
version(NETBSD)     version=SunnyishOS;
version(NEED_FIND_LIMIT) version=SomethingMeaningful;
version(UNIX_LIKE)       version=SomethingMeaningful;

version(SomethingMeaningful)
{
// ugly junk...
}
// etc...
======================

To this:

======================
module MyGC;

version(SUNOS5SIGS || IRIX5 || HURD || NETBSD)
    version=SunnyishOS;

version(NEED_FIND_LIMIT || UNIX_LIKE)
{
    // Alternatively, we could blah blah blah...
    version(SunnyishOS)
    {
        version( __STDC__ &&  GC_IRIX_THREADS) public import 
MyGC_SunnyishOS_STDC_IrixThreads;
        version( __STDC__ && !GC_IRIX_THREADS) public import 
MyGC_SunnyishOS_STDC_NonIrixThreads;
        version(!__STDC__ &&  GC_IRIX_THREADS) public import 
MyGC_SunnyishOS_KR_IrixThreads;
        version(!__STDC__ && !GC_IRIX_THREADS) public import 
MyGC_SunnyishOS_KR_NonIrixThreads;
    }
    else version(OSF1)
    {
        version( __STDC__ &&  GC_IRIX_THREADS) public import 
MyGC_OSF1_STDC_IrixThreads;
        version( __STDC__ && !GC_IRIX_THREADS) public import 
MyGC_OSF1_STDC_NonIrixThreads;
        version(!__STDC__ &&  GC_IRIX_THREADS) public import 
MyGC_OSF1_KR_IrixThreads;
        version(!__STDC__ && !GC_IRIX_THREADS) public import 
MyGC_OSF1_KR_NonIrixThreads;
    }
    //etc...
    else
    {
        version( __STDC__) public import MyGC_MISC_STDC;
        version(!__STDC__) public import MyGC_MISC_KR;
    }
}
//etc...
======================

The point is, the current semantics for D's version() are *plenty* 
susceptible to most of same versioning mess as C's #if/#ifdef, and in some 
cases (such as ||), even worse. With either style, the solution is exactly 
the same as any other chunk of messy code: Clean it up! Not only is gimping 
the version-control mechanism the wrong solution, it doesn't even solve the 
problem anyway.





More information about the Digitalmars-d mailing list