February 10, 2009
On Wed, 11 Feb 2009 02:16:16 +0300, Walter Bright <newshound1@digitalmars.com> wrote:

> Nick Sabalausky wrote:
>> This strikes me as throwing away the baby with the bathwater. If your code starts degenerating towards a versioning rat's nest, then the solution is to take a moment and refactor it into a larger granularity, not to throw away features that are useful in moderation.
>
> True, but that never, ever happens. It's always "I can get my few additions to this rat's nest in and just get it working, and worry about proper abstraction later."
>
>>> 1. Why not version(!feature) ?
>>>
>>> Because cognitive studies show that people just don't see the negation.
>>  Isn't that moreso a case against the ! operator in general? I don't see how "if(!blah)" is any less susceptible to overlooking the ! than "version(!blah)".
>
> Yes, you're right, it's both a minor and an inconsistent point, despite it being right <g>.
>
>
>>> Secondly, when you see things like:
>>>
>>>     version (!linux) { ... }
>>>
>>> they're almost always wrong. Versions should be positive things, because a version is something that is being build - one doesn't craft a makefile to build a notLinux.
>>  True, we don't build to a NotLinux, but we do however build to "everything except platform X".
>
> We do, and they're always wrong. I always find when doing that and porting to another machine that those #if !linux must be reengineered. I have switched entirely over to:
>
> #if Windows
>     ...
> #elif linux
>     ...
> #elif __APPLE__
>     ...
> #else
>     assert(0); // fix when adding a new platform
> #endif
>
> because otherwise I *miss* sections that need updating when porting to a new platform, and have a bug that needs tracking down.
>
> The point is, when writing portable code, especially code that will be ported by others, you have *no idea* what platforms will fall into the "notLinux" section and what peculiarities those platforms might have. It works much better to have the compiler ding me when it encounters platform specific code that doesn't have a section for the platform I'm compiling for.
>
>> And I don't see how a "LinuxOrMac" (or worse yet, a "LinuxOr_MacAndSomeOtherCharacteristic_") is any more or less of a realistic platform than a "NotLinux".
>
> Based on my experience porting code, LinuxOrMac is a far better solution (i.e. faster to code, faster to port, fewer bugs) than notWindows.
>
>
>> Also, you didn't respond to the concerns about typos in a version identifier. Can we assume you agree that's a problem?
>
> It's a problem without a solution unless you propose adding some sort of declaration syntax for all the version identifiers.

Why did you catch that controversial !Linux example? Just imagine it is !HasFeatureX:

version(!HasFeatureX) {
  ...
}

is so much more intuitive than

version (HasFeatureX) {
} else {
  ...
}

February 10, 2009
On Wed, 11 Feb 2009 12:16:16 +1300, Walter Bright <newshound1@digitalmars.com> wrote:

> Nick Sabalausky wrote:
>> This strikes me as throwing away the baby with the bathwater. If your code starts degenerating towards a versioning rat's nest, then the solution is to take a moment and refactor it into a larger granularity, not to throw away features that are useful in moderation.
>
> True, but that never, ever happens. It's always "I can get my few additions to this rat's nest in and just get it working, and worry about proper abstraction later."
>
>>> 1. Why not version(!feature) ?
>>>
>>> Because cognitive studies show that people just don't see the negation.
>>  Isn't that moreso a case against the ! operator in general? I don't see how "if(!blah)" is any less susceptible to overlooking the ! than "version(!blah)".
>
> Yes, you're right, it's both a minor and an inconsistent point, despite it being right <g>.
>
>
>>> Secondly, when you see things like:
>>>
>>>     version (!linux) { ... }
>>>
>>> they're almost always wrong. Versions should be positive things, because a version is something that is being build - one doesn't craft a makefile to build a notLinux.
>>  True, we don't build to a NotLinux, but we do however build to "everything except platform X".
>
> We do, and they're always wrong. I always find when doing that and porting to another machine that those #if !linux must be reengineered. I have switched entirely over to:
>
> #if Windows
>     ...
> #elif linux
>     ...
> #elif __APPLE__
>     ...
> #else
>     assert(0); // fix when adding a new platform
> #endif
>
> because otherwise I *miss* sections that need updating when porting to a new platform, and have a bug that needs tracking down.
>
> The point is, when writing portable code, especially code that will be ported by others, you have *no idea* what platforms will fall into the "notLinux" section and what peculiarities those platforms might have. It works much better to have the compiler ding me when it encounters platform specific code that doesn't have a section for the platform I'm compiling for.
>
>> And I don't see how a "LinuxOrMac" (or worse yet, a "LinuxOr_MacAndSomeOtherCharacteristic_") is any more or less of a realistic platform than a "NotLinux".
>
> Based on my experience porting code, LinuxOrMac is a far better solution (i.e. faster to code, faster to port, fewer bugs) than notWindows.
>
>
>> Also, you didn't respond to the concerns about typos in a version identifier. Can we assume you agree that's a problem?
>
> It's a problem without a solution unless you propose adding some sort of declaration syntax for all the version identifiers.

It would be nice if version just evaluated to a bool of true if the version is defined or false otherwise and you have to use it in static if statements, drop the old syntax. This would be very consistent, powerfull and if that is considered bad style then it shouldn't have to be used in the same way goto statements are rarely used yet they still exist.
February 11, 2009
Denis Koroskin wrote:
> Why did you catch that controversial !Linux example? Just imagine it is !HasFeatureX:
> 
> version(!HasFeatureX) {
>   ...
> }
> 
> is so much more intuitive than
> 
> version (HasFeatureX) {
> } else {
>   ...
> }


This is to combat the pernicious problem of having features that subtract code <g>.
February 11, 2009
Steven Schveighoffer wrote:
> "Walter Bright" wrote
> 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.  On top of that, the way versioning works, you need 2^4 * 5 different statements:
> 
> version(A)
> {
>    version = AorB;
>    version = AorC;
>    version = AorD;
>    version = AorE;
>    version = AorBorC;
>    version = AorBorD;
>    // ad nauseum.
> }
> version(B)
> {
>    // oh fun! let's do it again!!!!
>    version = AorB;
>    version = BorC;
>    version = BorD;
>    ...
> }
> 
> 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!

But this is clearly the wrong way of cutting the pie. What you need is define features that are supported by some of the versions. You don't need to judge in terms of logical operators between versions.

version (A) version = canBeUsedToPickupChicks;
version (C) version = canBeUsedToPickupChicks;
// version (B) no good


Andrei
February 11, 2009
On Tue, 10 Feb 2009 14:01:29 -0800, Walter Bright wrote:

> Derek Parnell wrote:

>>  ... it creates a new problem; code
>> duplication.
> 
> I don't think that duplicating a small run of code is a problem.


My apology. The problem is more than run-time performance issues. The more pressing problem, IMHO, is the one of maintenance costs. Duplicated code is a significant cost burden. Not only may each duplication require updating, but there is extra effort in analyizing every duplicate to see if it *does* need updating. And every act of updating increases the opportunity for introducing bugs.

Coders need languages that help them do their job, and one way to help is to reduce the need for duplicated code.


>> Duplicating (nearly all of a) source file is NOT, repeat NOT, a
>> satisfatory solution.
> 
> I'm not insensitive to this as I do it myself in maintaining Phobos. It is a problem, but not a huge one. I find that the meld utility (on linux) makes this chore a snap.

Because of D's limited support for text macros, I am using third party tools to get me out of this problem too.

-- 
Derek Parnell
Melbourne, Australia
skype: derek.j.parnell
February 11, 2009
Walter Bright Wrote:


> 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.

Would you be willing to introduce an alternative to /+ +/ which would be treated differently by the D1 and D2 compilers? Here are some examples with no attempt at creativity:
beginD1 endD1
D1 D1 (works like string delimiters)
/D2 D2/
February 11, 2009
Denis Koroskin wrote:
> Does it look any better? No way!

Of course doing it that way doesn't look any better, because it still just replicates the C preprocessor style of doing it.

A far better solution is to create a series of modules:

gcnetbsd.d
gchurd.d
gcsunos5.d
...

and inside each one put the specifics for that particular system. The huge advantage of this is that if I want to create a BrightBSD operating system, I just have to write a:

gcbrightbsd.d

rather than trying to carefully fold it into that conditional compilation mess without inadvertently breaking other platform support. (And I cannot even tell if I broke the SunOS5 platform support or not, because I don't have a SunOS5 platform to test it on.)


> The story is not about different functionality on different platforms but rather about a common code which is 98% the same on all the platforms and is different in *small* details.
> For example, I'd like to make my library D1 and D2 compatible. Do you suggest me to maintain 2 different libraries? This is ridiculous, and that's why there is no Tango2 release yet - there is *no* point in supporting such a large library as Tango (or DWT) for two language versions without a sane versioning mechanism.

There are two very different things going on here. One is accounting for differences in the *language*, the other is about generating different builds based on language independent different desired features and platform characteristics.

I agree that version is not a solution to the D1/D2 language difference problem, and furthermore contend it cannot be coerced into being one. The only real solution, other than maintaining separate source files, is to use a text macro preprocessor.

(Sadly, the C preprocessor cannot be used, because it is a tokenizing textual preprocessor, and C preprocessor tokens are different from D tokens.)
February 11, 2009
"Walter Bright" <newshound1@digitalmars.com> wrote in message news:gmsvb8$e1p$1@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.


February 11, 2009
Andrei Alexandrescu wrote:
> But this is clearly the wrong way of cutting the pie.

Right, and I also suggest that it is the C preprocessor way of doing versioning. We're all deep in the C preprocessor rut, and find it difficult to think another way.

For example, I learned to program in Fortran. My early C code looks an awful lot like Fortran. People who came to C from Pascal would write things like:

#define BEGIN {
#define END }
February 11, 2009
"Walter Bright" <newshound1@digitalmars.com> wrote in message news:gmt6l0$rff$1@digitalmars.com...
> Denis Koroskin wrote:
>> Does it look any better? No way!
>
> Of course doing it that way doesn't look any better, because it still just replicates the C preprocessor style of doing it.
>

Which just goes to show that the restrictions you've placed on D's version() (in order to eliminate rat's nest versioning) DON'T eliminate rat's nest versioning.

> A far better solution...

And we can come up with better solutions for C as well. Granted, the optimal D solution is going to be much better than the optimal C solution, but it won't be due to version()'s lack of !, ||, &&, etc...