www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why version() ?

reply bobef <bobef nospam-abv.bg> writes:
I was thinking... what is the point of version() ? It is so inflexible. There
is no even version(!...). Why not "static if(version(DMD))" or static
if(is(version == DMD))?

Regards,
bobef
Feb 10 2009
next sibling parent Trass3r <mrmocool gmx.de> writes:
bobef schrieb:
 Why not "static if(version(DMD))" or static if(is(version == DMD))?
 

Cause nobody wants to write something ugly like that every time. version(DMD) is nice and clear.
Feb 10 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
bobef wrote:
 I was thinking... what is the point of version() ? It is so inflexible. There
is no even version(!...). Why not "static if(version(DMD))" or static
if(is(version == DMD))?
 
 Regards,
 bobef

One of my pet peeves. The only advantages of version() over static if() are: 1. You can pass version flags as command line parameters 2. It is shorter. Some severe disadvantages of version(): 1. They can't contain expressions (like "version(linux && mac)"). 2. Typos are not catched! "version(linxu)" will just silently compile. Proposal to solve these problems: Remove "version()" and replace it by "static if()" (actually, it would make sense to turn version into an alias for static if). Introduce const declarations, which get their value from commandline: //command line for dmd to set the constant cLinux dmd -version cLinux=true //define a const variable, whose value is taken from the commandline //they always need to be declared, the compiler shouldn't just //arbitrarily introduce new symbols into the modules' scope! //(exact syntax needs to be discussed) const bool cLinux = $cmdarg; //instead of version(linux) //unlike version, arbitrary expressions are allowed static if(cLinux) { //Linux code goes here } static if(cLinxu) { //oops, typo, the compiler will loudly complain! As an additional feature, you could allow passing of additional data types instead of only bool. Strings, for example. Unlike version(), cLinux always needs to be defined on the command line. I don't consider this a disadvantage. I'd also leave debug() as it is.
Feb 10 2009
next sibling parent reply Trass3r <mrmocool gmx.de> writes:
grauzone schrieb:
 Some severe disadvantages of version():
 1. They can't contain expressions (like "version(linux && mac)").

version(linux) version(mac) { } But yeah, that's a feature that could be added.
Feb 10 2009
parent reply Don <nospam nospam.com> writes:
Trass3r wrote:
 grauzone schrieb:
 Some severe disadvantages of version():
 1. They can't contain expressions (like "version(linux && mac)").

version(linux) version(mac) { } But yeah, that's a feature that could be added.

A&&B is not so terrible, since you can do it by nesting, as above. The big problem is A || B. It creates a mess.
Feb 10 2009
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 10 Feb 2009 13:08:03 +0100, Don wrote:

 A&&B is not so terrible, since you can do it by nesting, as above.
 The big problem is A || B. It creates a mess.

version(A) version = AorB; version(B) version = AorB; version(AorB) { . . . } -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 10 2009
parent reply Don <nospam nospam.com> writes:
Derek Parnell wrote:
 On Tue, 10 Feb 2009 13:08:03 +0100, Don wrote:
 
 A&&B is not so terrible, since you can do it by nesting, as above.
 The big problem is A || B. It creates a mess.

version(A) version = AorB; version(B) version = AorB; version(AorB) { . . . }

places. And you've had to create an essentially meaningless temporary. [1] Then when you have a more complex expression, especially where A and B appear more than once, it gets disgusting. [1] I know Walter's idea is that you create meaningful identifiers, but in practice this kind of versioning can be a workaround. EG B is "everything except WindowsME".
Feb 10 2009
next sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Denis Koroskin wrote:
 [snip]
 
 Other use-case I came across recently reading .NET Framework source code:
 
 class String :
    IFoo,
    IBar,
    IBaz,
 #if SUPPORT_GENERICS
    IComparable<Foo>,
 #endif
    IWhee
 {
    // *very* large amount of code
 }
 
 How would you port it to D without code duplication and meaningless
 intermediate classes?

I think I once tried something where I was building a list of interface types as a tuple, then tried this: class Foo : InterfaceTuple { ... } Didn't work, though. -- Daniel
Feb 10 2009
prev sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:14:00 +0300, Don <nospam nospam.com> wrote:
 
 Derek Parnell wrote:
 On Tue, 10 Feb 2009 13:08:03 +0100, Don wrote:

 A&&B is not so terrible, since you can do it by nesting, as above.
 The big problem is A || B. It creates a mess.

version(B) version = AorB; version(AorB) { . . . }

places. And you've had to create an essentially meaningless temporary. [1] Then when you have a more complex expression, especially where A and B appear more than once, it gets disgusting. [1] I know Walter's idea is that you create meaningful identifiers, but in practice this kind of versioning can be a workaround. EG B is "everything except WindowsME".

Version has so many shortcomings that it should definitely be redesigned. My biggest complaint is that code inside version () {} block should be semantically correct.

Just syntactically correct.
Feb 10 2009
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
grauzone:
 Unlike version(), cLinux always needs to be defined on the command line. 
 I don't consider this a disadvantage.

I think I don't like that. But I may like something like this: static if (__version__ == DMD) {...} more than: version(DMD) {...} It's longer, but it doesn't need the version keyword, it's more uniform and easy anyway to remember and use, and it can be used in more refined ways. I may even appreciate other boolean constants like: static if (__debug__) {...} static if (__unittest__) {...} static if (__ismain__) {...} Where bud and similar tools define __ismain__ as true only inside the main module. Bye, bearophile
Feb 10 2009
prev sibling next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
grauzone wrote:
 Some severe disadvantages of version():
 1. They can't contain expressions (like "version(linux && mac)").

Unless Apple has plans to s/BSD/Linux/ in MacOS that I'm not aware of, version(linux || mac) would probably be more useful... Allowing boolean expressions (using only version identifiers) in version() would be a very welcome feature. I'm not sure about the rest of your post though.
Feb 10 2009
parent reply grauzone <none example.net> writes:
Frits van Bommel wrote:
 grauzone wrote:
 Some severe disadvantages of version():
 1. They can't contain expressions (like "version(linux && mac)").

Unless Apple has plans to s/BSD/Linux/ in MacOS that I'm not aware of, version(linux || mac) would probably be more useful... Allowing boolean expressions (using only version identifiers) in version() would be a very welcome feature. I'm not sure about the rest of your post though.

Well, my argument is that CTFE already provides all version features and even more. version is redundant, and even dangerous, because it can't catch typos in version identifiers. Actually, C/C++ has something similar: #if (is similar to static if) versus #ifdef (is similar to version). ffmpeg recently switched from using #ifdef to #if.
Feb 10 2009
parent Sergey Gromov <snake.scaly gmail.com> writes:
Tue, 10 Feb 2009 13:34:35 +0100, grauzone wrote:

 Frits van Bommel wrote:
 grauzone wrote:
 Some severe disadvantages of version():
 1. They can't contain expressions (like "version(linux && mac)").

Unless Apple has plans to s/BSD/Linux/ in MacOS that I'm not aware of, version(linux || mac) would probably be more useful... Allowing boolean expressions (using only version identifiers) in version() would be a very welcome feature. I'm not sure about the rest of your post though.

Well, my argument is that CTFE already provides all version features and even more. version is redundant, and even dangerous, because it can't catch typos in version identifiers. Actually, C/C++ has something similar: #if (is similar to static if) versus #ifdef (is similar to version). ffmpeg recently switched from using #ifdef to #if.

#if is no different from #ifdef because any undefined identifier evaluates to 0 within the #if context.
Feb 15 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 10 Feb 2009 17:14:00 +0300, Don <nospam nospam.com> wrote:

 Derek Parnell wrote:
 On Tue, 10 Feb 2009 13:08:03 +0100, Don wrote:

 A&&B is not so terrible, since you can do it by nesting, as above.
 The big problem is A || B. It creates a mess.

version(B) version = AorB; version(AorB) { . . . }

places. And you've had to create an essentially meaningless temporary. [1] Then when you have a more complex expression, especially where A and B appear more than once, it gets disgusting. [1] I know Walter's idea is that you create meaningful identifiers, but in practice this kind of versioning can be a workaround. EG B is "everything except WindowsME".

Version has so many shortcomings that it should definitely be redesigned. My biggest complaint is that code inside version () {} block should be semantically correct. It creates many obstacles and disallows good use-cases: - you cannot mix D1, D2 and (in future) D3 in a single source code file without helf of mixins. - you cannot mix asm for different platforms in one source code file (because DMD doesn't understand ARM assembly, for instance) - etc Other use-case I came across recently reading .NET Framework source code: class String : IFoo, IBar, IBaz, #if SUPPORT_GENERICS IComparable<Foo>, #endif IWhee { // *very* large amount of code } How would you port it to D without code duplication and meaningless intermediate classes?
Feb 10 2009
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"grauzone" <none example.net> wrote in message 
news:gmrpj5$1hea$1 digitalmars.com...
 bobef wrote:
 I was thinking... what is the point of version() ? It is so inflexible. 
 There is no even version(!...). Why not "static if(version(DMD))" or 
 static if(is(version == DMD))?

 Regards,
 bobef

One of my pet peeves. The only advantages of version() over static if() are: 1. You can pass version flags as command line parameters 2. It is shorter. Some severe disadvantages of version(): 1. They can't contain expressions (like "version(linux && mac)"). 2. Typos are not catched! "version(linxu)" will just silently compile. Proposal to solve these problems: Remove "version()" and replace it by "static if()" (actually, it would make sense to turn version into an alias for static if). Introduce const declarations, which get their value from commandline: //command line for dmd to set the constant cLinux dmd -version cLinux=true //define a const variable, whose value is taken from the commandline //they always need to be declared, the compiler shouldn't just //arbitrarily introduce new symbols into the modules' scope! //(exact syntax needs to be discussed) const bool cLinux = $cmdarg; //instead of version(linux) //unlike version, arbitrary expressions are allowed static if(cLinux) { //Linux code goes here } static if(cLinxu) { //oops, typo, the compiler will loudly complain! As an additional feature, you could allow passing of additional data types instead of only bool. Strings, for example. Unlike version(), cLinux always needs to be defined on the command line. I don't consider this a disadvantage. I'd also leave debug() as it is.

I wholeheartedly agree with this 100%. I'd also have to agree with Denis's point about the semantic/syntactic restrictions: "Denis Koroskin" <2korden gmail.com> wrote in message news:op.uo433jipo7cclz proton.creatstudio.intranet...
 My biggest complaint is that code inside version () {} block should be 
 semantically correct. It creates many obstacles and disallows good 
 use-cases:
 - you cannot mix D1, D2 and (in future) D3 in a single source code file 
 without helf of mixins.
 - you cannot mix asm for different platforms in one source code file 
 (because DMD doesn't understand ARM assembly, for instance)
 - etc

Feb 10 2009
prev sibling next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
bobef wrote:

 I was thinking... what is the point of version() ? It is so inflexible. 

if(is(version == DMD))?
 
 Regards,
 bobef

What would be the difference except more verbosity? You can use version(x) { } else { } The rationale of version() is a simple, coarse grained conditional compilation of features. It is not meant for metaprogramming like static if is.
Feb 10 2009
prev sibling next sibling parent reply "Tim M" <a b.com> writes:
On Wed, 11 Feb 2009 00:28:21 +1300, bobef <bobef nospam-abv.bg> wrote:

 I was thinking... what is the point of version() ? It is so inflexible.  
 There is no even version(!...). Why not "static if(version(DMD))" or  
 static if(is(version == DMD))?

 Regards,
 bobef

You could try something like this: module Vers; template vers(char[] V) { mixin("version(" ~ V ~ ") { const bool vers = true; } else { const bool vers = false; }"); } int main() { static if(vers!("X86_64")) { long a; pragma(msg, "64 bit"); } else { int a; pragma(msg, "32 bit"); } a = 2; return a; }
Feb 10 2009
next sibling parent "Tim M" <a b.com> writes:
It gets even better with constants and works in static asserts too:


module Vers;

template vers(char[] V)
{
       mixin("version(" ~ V ~ ")
       {
             const bool vers = true;
       }
       else
       {
             const bool vers = false;
       }");
}

const DigitalMars = "DigitalMars";
const X86 = "X86";
const X86_64 = "X86_64";
const Windows = "Windows";
const Win32 = "Win32";
const Win64 = "Win64";
const LittleEndian = "LittleEndian";
const BigEndian = "BigEndian";
const D_Coverage = "D_Coverage";
const D_Ddoc = "D_Ddoc";
const D_InlineAsm_X86 = "D_InlineAsm_X86";
const D_InlineAsm_X86_64 = "D_InlineAsm_X86_64";
const D_LP64 = "D_LP64";
const D_PIC = "D_PIC";
const none = "none";
const all = "all";

int main()
{
       const bool forceLong = false;
       static if(vers!(X86_64) || forceLong)
       {
             long a;
             pragma(msg, "64 bit");
       }
       else
       {
             int a;
             pragma(msg, "32 bit");
       }

       static assert(vers!(LittleEndian) || vers!(BigEndian), "You are a  
weird endian");



       a = 2;

       return a;
}
Feb 10 2009
prev sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Tim M wrote:
 On Wed, 11 Feb 2009 00:28:21 +1300, bobef <bobef nospam-abv.bg> wrote:
 
 I was thinking... what is the point of version() ? It is so 
 inflexible. There is no even version(!...). Why not "static 
 if(version(DMD))" or static if(is(version == DMD))?

 Regards,
 bobef

You could try something like this: module Vers; template vers(char[] V) { mixin("version(" ~ V ~ ") { const bool vers = true; } else { const bool vers = false; }"); } int main() { static if(vers!("X86_64")) { long a; pragma(msg, "64 bit"); } else { int a; pragma(msg, "32 bit"); } a = 2; return a; }

You beat me to it. I actually use this a little here and there. But honestly, I find that the cases where I just want 'version(X){...}else{...}' far outweigh the cases where I want 'X && Y' or the like. While it is technically a hack/workaround, I've been perfectly happy with it. Now, what would be nice, would be if version() could appear in places it currently can't, following a few rules. In mid-statement, for example, so long as its body is a complete expression. And then the same for mixin expressions. Might be daydreaming too much, there. Imagine though... template vers (char[] V) { const vers = mixin("version("~V~") {true} else {false}") ; } Add to that a deep shorthand syntax a la: template vers (char[] V) = mixin("version("~V~") {true} else {false}") ; Okay, I'll come down from the clouds now. -- Chris Nicholson-Sauls
Feb 10 2009
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
bobef wrote:
 I was thinking... what is the point of version() ? It is so inflexible. There
is no even version(!...). Why not "static if(version(DMD))" or static
if(is(version == DMD))?
 
 Regards,
 bobef

version() is MUCH older than static if. It dates from a very early time in D's history. I requested module-scope static if, and Walter said "that's probably almost impossible to implement", but he did it anyway <g>.
Feb 10 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:
 I requested module-scope static if, and Walter said "that's probably 
 almost impossible to implement", but he did it anyway <g>.

Walter does almost the impossible, sometimes. I'll have to remember this :-) Bye, bearophile
Feb 10 2009
parent reply Don <nospam nospam.com> writes:
bearophile wrote:
 Don:
 I requested module-scope static if, and Walter said "that's probably 
 almost impossible to implement", but he did it anyway <g>.

Walter does almost the impossible, sometimes. I'll have to remember this :-) Bye, bearophile

The traditional way to get an engineer to do something, is to tell him you believe it's impossible. Works very well with Walter. Also Walter tends not to promise to do something, unless he's already finished it <g>.
Feb 10 2009
parent reply John Reimer <terminal.node gmail.com> writes:
Hello Don,

 bearophile wrote:
 
 Don:
 
 I requested module-scope static if, and Walter said "that's probably
 almost impossible to implement", but he did it anyway <g>.
 

this :-) Bye, bearophile

you believe it's impossible. Works very well with Walter. Also Walter tends not to promise to do something, unless he's already finished it <g>.

So I assume it must follow that engineers should never be taught computational complexity theory... determining that a problem is intractable might affect their attempts at ingenious solutions that shouldn't exist. :D -JJR
Feb 10 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 So I assume it must follow that engineers should never be taught 
 computational complexity theory... determining that a problem is 
 intractable might affect their attempts at ingenious solutions that 
 shouldn't exist. :D

I've suspected that's why breakthroughs are made by the young, as they don't yet understand what's impossible.
Feb 10 2009
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
bobef wrote:
 I was thinking... what is the point of version() ? It is so
 inflexible. There is no even version(!...). Why not "static
 if(version(DMD))" or static if(is(version == DMD))?

The version statement is designed to be inflexible. It's designed to encourage abstracting and modularizing version differences, rather than encouraging line-by-line differences. I've used #if/#endif for decades. Over time, it *always* degenerates into an utter rat's nest of nested, incomprehensible complexity (and this includes my own code). For a lovely example of how bad it can get, take a look at the source code to Hans Boehm's garbage collector. I defy anyone to figure it out without running the code through the preprocessor first to see which statements are actually getting compiled. A far better way to do versioning is to find the abstraction lines separating versions, and put version specific code in separate modules that are then imported. It makes for nice, clean, understandable code. Static if can be used for version control, but that is not what it's for. Static if is for use inside templates to control code generation based on template parameters, not for producing different versions of the program. 1. Why not version(!feature) ? Because cognitive studies show that people just don't see the negation. 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. 2. version (A || B) can be done as: version (A) version=AorB; version (B) version=AorB; 3. version (A && B) can be done as: version (A) version (B) { ... } though if you find yourself doing that a lot, I suggest abstracting away the A and B version into its own AandB version. 4. Conditional compilation can be done with version(all) and version(none). 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.
Feb 10 2009
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 10 Feb 2009 13:14:24 -0800, Walter Bright wrote:

 The version statement is designed to be inflexible. It's designed to 
 encourage abstracting and modularizing version differences, rather than 
 encouraging line-by-line differences.

I understand and support this view. However, it creates a new problem; code duplication. Is it possible to provide some syntax or technique to avoid having to cut&paste /small/ runs of code lines. Larger sections of code can be separated into functions or modules, but it because problematic when dealing with small sections (a few lines) of code. The overheads of function calling can be an issue.
 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.

Agreed, this is a problem. How can it be solved? The problem is often found when supporting V1 and V2 code in the same source file. The string mixin trick is NOT, repeat NOT, a satisfactory solution. Duplicating (nearly all of a) source file is NOT, repeat NOT, a satisfatory solution. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 10 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Tue, 10 Feb 2009 13:14:24 -0800, Walter Bright wrote:
 
 The version statement is designed to be inflexible. It's designed to 
 encourage abstracting and modularizing version differences, rather than 
 encouraging line-by-line differences.

I understand and support this view. However, it creates a new problem; code duplication. Is it possible to provide some syntax or technique to avoid having to cut&paste /small/ runs of code lines. Larger sections of code can be separated into functions or modules, but it because problematic when dealing with small sections (a few lines) of code. The overheads of function calling can be an issue.

I don't think that duplicating a small run of code is a problem. If it's larger than a small run, it can be abstracted into a function (the overhead is not an issue, as small functions are inlined).
 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.

Agreed, this is a problem. How can it be solved? The problem is often found when supporting V1 and V2 code in the same source file. The string mixin trick is NOT, repeat NOT, a satisfactory solution.

I agree that the mixin trick is horrific.
 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.
Feb 10 2009
parent reply Derek Parnell <derek psych.ward> writes:
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
Feb 10 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Tue, 10 Feb 2009 14:01:29 -0800, Walter Bright wrote:
 
 Derek Parnell wrote:

  ... it creates a new problem; code
 duplication.


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.

Let's take an example, like the enum for O_XXXX in std.c.linux.linux. Some of those values are common between platforms, and some are unique to particular platforms. So you might be tempted to write: enum { O_RDONLY = 0, O_WRONLY = 1, O_RDWR = 2, O_CREAT = 0100, version(OSX) O_SYMLINK = 0x200000, } instead of: version (linux) { enum { O_RDONLY = 0, O_WRONLY = 1, O_RDWR = 2, O_CREAT = 0100, } } else version (OSX) { enum { O_RDONLY = 0, O_WRONLY = 1, O_RDWR = 2, O_CREAT = 0100, O_SYMLINK = 0x200000, } } else { static assert(0); // need platform support } The first version is definitely shorter. But is it easier to maintain? I argue that it is *harder* and *buggier* to maintain. Let's say I am using linux and I need to add O_APPEND to it. I just stuff it in like: enum { O_RDONLY = 0, O_WRONLY = 1, O_RDWR = 2, O_CREAT = 0100, O_APPEND = 02000, version(OSX) O_SYMLINK = 0x200000, } and it works great for linux. Now I port my code to OSX, and it mysteriously dies. After much frustration, I discover that O_APPEND for OSX is 8, not 02000. (Yes, this kind of thing has happened to me. I only found it by suspecting the problem, and then going through every fscking definition comparing it to the one in the system .h file. Yeech.) Now let's try the other way. I add O_APPEND to the linux branch, and linux works great. Now I port to OSX, and the compiler dies with "O_APPEND is undefined". I know immediately exactly what is wrong, look up the .h file, and insert the right O_APPEND into the OSX version of the declaration. Furthermore, when I build a FreeBSD version, the compiler bings at me for every declaration that needs some porting attention, instead of silently using the wrong values. This would be even better if the OSX and linux declarations were split into separate "personality" modules. That way you can develop happily on OSX without fear of accidentally breaking linux support. You can defer dealing with the linux version until you actually on the linux machine and are in an efficient position to take care of it.
 Coders need languages that help them do their job, and one way to help is
 to reduce the need for duplicated code.

I know there's a risk by getting in the way of programmers wanting to do things a particular way. To do it, I have to be pretty convinced that there is a better way. The C preprocessor is like crack, everyone knows it's bad but they snort it anyway because it feels so good <g>. C++ was supposed to get a bunch of features that obviate the preprocessor, but have a look at the premier C++ library - Boost - which uses the preprocessor heavily. Boost also uses every last iota of what the preprocessor can do, because if your cpp implementation is not 100% most of Boost will not compile (I know this from experience, DMC++ is 100% now).
 Duplicating (nearly all of a) source file is NOT, repeat NOT, a
 satisfatory solution.

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.

meld is particularly nice. Andrei showed it to me: http://www.linux.com/feature/61372
Feb 10 2009
next sibling parent Sean Kelly <sean invisibleduck.org> writes:
== Quote from Walter Bright (newshound1 digitalmars.com)'s article
...
 Let's take an example, like the enum for O_XXXX in std.c.linux.linux.
 Some of those values are common between platforms, and some are unique
 to particular platforms. So you might be tempted to write:

 The first version is definitely shorter. But is it easier to maintain? I
 argue that it is *harder* and *buggier* to maintain.

This is definitely true of header modules. There's absolutely no way I could manage the Posix headers in Tango/druntime with the non-duplicating approach.
 This would be even better if the OSX and linux declarations were split
 into separate "personality" modules. That way you can develop happily on
 OSX without fear of accidentally breaking linux support. You can defer
 dealing with the linux version until you actually on the linux machine
 and are in an efficient position to take care of it.

With version blocks, working with one version shouldn't break another version anyway, unless I'm misunderstanding your point. Sean
Feb 10 2009
prev sibling next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Derek Parnell wrote:
 Because of D's limited support for text macros, I am using third party
 tools to get me out of this problem too.

http://www.linux.com/feature/61372

I suggest UltraCompare, if you're a Windows user. Sean
Feb 10 2009
parent "Nick Sabalausky" <a a.a> writes:
"Sean Kelly" <sean invisibleduck.org> wrote in message 
news:gmt9pl$11mt$1 digitalmars.com...
 == Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Derek Parnell wrote:
 Because of D's limited support for text macros, I am using third party
 tools to get me out of this problem too.

http://www.linux.com/feature/61372

I suggest UltraCompare, if you're a Windows user.

I've been a big fan of Beyond Compare for a long time now. I'll have to take a look at UltraCompare.
Feb 10 2009
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 10 Feb 2009 17:16:22 -0800, Walter Bright wrote:

 Let's take an example, like the enum for O_XXXX in std.c.linux.linux. 
 Some of those values are common between platforms, and some are unique 
 to particular platforms...

Yes, the duplicated code is a better approach here because the apparent common code is the key in this example. The lines that are the same are only coincidently common and and not common due to any inate factors of the operating system or data being declared.
 This would be even better if the OSX and linux declarations were split 
 into separate "personality" modules. 

Agreed. The example I offer here is more the sort of thing that it seems you are also finding when maintaining Phobos ... Current my code loks like this ... //--------------- Using version() w/o duplicating code -------- //------------------------------------------------------- int RunCommand(string pExeName, string pCommand, runCallBack pCallBack=null) //------------------------------------------------------- { version(Windows) { if (std.path.getExt(pExeName).length == 0) pExeName ~= ".exe"; } if (util.pathex.IsRelativePath(pExeName) == True) { string lExePath; lExePath = util.pathex.FindFileInPathList("PATH", pExeName); if (util.str.ends(lExePath, std.path.sep) == False) lExePath ~= std.path.sep; version(Windows) pExeName = util.pathex.CanonicalPath(lExePath ~ pExeName, False, False); version(Posix) pExeName = util.pathex.CanonicalPath(lExePath ~ pExeName, False, True); } if (util.file2.FileExists(pExeName) == false) { throw new FileExException(std.string.format( "Cannot find application '%s' to run", pExeName)); } return RunCommand( util.str.enquote(pExeName) ~ " " ~ pCommand, pCallBack); } //------------------------------------------------------- int RunCommand(string pCommand, runCallBack pCallBack = null) //------------------------------------------------------- { int lRC; if (pCallBack !is null) { lRC = pCallBack(1, pCommand, 0); if (lRC != 0) return 0; } lRC = system(std.string.toStringz(pCommand)); version(Posix) lRC = ((lRC & 0xFF00) >> 8); if (pCallBack !is null) pCallBack(2, pCommand, lRC); return lRC; } But if I split the opsys versioning out I get ... //--------------- Using version() with duplicating code -------- alias int function(int, string, int) runCallBack; version(Windows) { //------------------------------------------------------- int RunCommand(string pExeName, string pCommand, runCallBack pCallBack=null) //------------------------------------------------------- { if (std.path.getExt(pExeName).length == 0) pExeName ~= ".exe"; if (util.pathex.IsRelativePath(pExeName) == True) { string lExePath; lExePath = util.pathex.FindFileInPathList("PATH", pExeName); if (util.str.ends(lExePath, std.path.sep) == False) lExePath ~= std.path.sep; pExeName = util.pathex.CanonicalPath(lExePath ~ pExeName, False, False); } if (util.file2.FileExists(pExeName) == false) { throw new FileExException(std.string.format( "Cannot find application '%s' to run", pExeName)); } return RunCommand( util.str.enquote(pExeName) ~ " " ~ pCommand, pCallBack); } //------------------------------------------------------- int RunCommand(string pCommand, runCallBack pCallBack = null) //------------------------------------------------------- { int lRC; if (pCallBack !is null) { lRC = pCallBack(1, pCommand, 0); if (lRC != 0) return 0; } lRC = system(std.string.toStringz(pCommand)); if (pCallBack !is null) pCallBack(2, pCommand, lRC); return lRC; } } version(Posix) { //------------------------------------------------------- int RunCommand(string pExeName, string pCommand, runCallBack pCallBack=null) //------------------------------------------------------- { if (util.pathex.IsRelativePath(pExeName) == True) { string lExePath; lExePath = util.pathex.FindFileInPathList("PATH", pExeName); if (util.str.ends(lExePath, std.path.sep) == False) lExePath ~= std.path.sep; pExeName = util.pathex.CanonicalPath(lExePath ~ pExeName, False, True); } if (util.file2.FileExists(pExeName) == false) { throw new FileExException(std.string.format( "Cannot find application '%s' to run", pExeName)); } return RunCommand( util.str.enquote(pExeName) ~ " " ~ pCommand, pCallBack); } //------------------------------------------------------- int RunCommand(string pCommand, runCallBack pCallBack = null) //------------------------------------------------------- { int lRC; if (pCallBack !is null) { lRC = pCallBack(1, pCommand, 0); if (lRC != 0) return 0; } lRC = system(std.string.toStringz(pCommand)); lRC = ((lRC & 0xFF00) >> 8); if (pCallBack !is null) pCallBack(2, pCommand, lRC); return lRC; } } Now we have almost twice the code and whenever an enhancement to the RunCommand function is needed, I must examine and correctly update twice as many lines now, because *most*, but not quite all, the logic is identical between both operating systems. This is the sort of scenario that will cause bugs to be introduced. The fine line that divides when to duplicate and when not to duplicate, is hard to see clearly. I tend to favour the less duplication approach, but only when it leads to lower maintenance costs. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 10 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 The fine line that divides when to duplicate and when not to duplicate, is
 hard to see clearly. I tend to favour the less duplication approach, but
 only when it leads to lower maintenance costs.

I agree it's hard to see clearly, and often can only be seen after attempting a couple of ports.
Feb 10 2009
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:gmt8t7$1042$1 digitalmars.com...
 Derek Parnell wrote:
 On Tue, 10 Feb 2009 14:01:29 -0800, Walter Bright wrote:

 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.

Let's take an example, like the enum for O_XXXX in std.c.linux.linux. Some of those values are common between platforms, and some are unique to particular platforms. So you might be tempted to write:

I have to admit, that's a very compelling example. I hadn't thought of anything like that. I guess the moral is "DRY is normally great, but be very careful with it when using it across multiple builds." But I'm still not sure we should be going that far. For instance: enum { version(Linux || OSX) { O_RDONLY = 0, O_WRONLY = 1, O_RDWR = 2, O_CREAT = 0100, } version(Linux) { O_APPEND = 02000, } version(OSX) { O_APPEND = 8, O_SYMLINK = 0x200000, } } That seems reasonable to me. Granted, with a small enum like that, completely replicating the whole enum would be fine, but imagine if it were something much larger, like MS or Apple error codes (though that may not be an ideal example since they're platform-specific). Unlike Walter's example, this method clearly allows for DRY, would would be quite an improvement on large enums. But I would argue that this method also provides for just as much robustness: Scenario 1: Porting to Windows, BSD, or Joe's Embedded OS: Walter's example results in an obvious compile-time failure at the enum. Mine might not do that (but maybe it *could* still do some sort of "version(Linux||OSX) {...} else fail" ), however mine does still cause an error anytime there's an attempt to call (or maybe even define) the "file open" function (Since there's no valid value for the paramater. Even the default value would be invalid). Scenario 2: Linux User Joe wants to add an "O_PREPEND" (Yea, I know that's *very* contrived): He's going to see three sections, Linux, Mac, and Both. The "Linux||OSX" part makes it clear to him that he should double-check the value of O_PREPEND on OSX if he wants to stick it in "Both", or else he knows he can just leave it in the Linux-only part or check with the project's manager. Later on, if Project Manager Suzy uses both systems and knows that O_PREPEND is the same on both, she can just move it to the "Both" section. Besides, without expression-level version(), the project's author might just as likely think "Shoot, this is stupid that D doesn't let me make this nicely DRY. But I really like DRY, so I guess I'll just resort to an external C-like pre-processor".
Feb 10 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 I have to admit, that's a very compelling example. I hadn't thought of 
 anything like that.

I didn't either, until I got bit by it <g>.
 I guess the moral is "DRY is normally great, but be very 
 careful with it when using it across multiple builds." But I'm still not 
 sure we should be going that far. For instance:
 
 enum
 {
     version(Linux || OSX)
     {
         O_RDONLY = 0,
         O_WRONLY = 1,
         O_RDWR = 2,
         O_CREAT = 0100,
     }
     version(Linux)
     {
         O_APPEND = 02000,
     }
     version(OSX)
     {
         O_APPEND = 8,
         O_SYMLINK = 0x200000,
     }
 }
 
 That seems reasonable to me.

Yeah, but I guarantee you that there will be some "common to all" ones and an irresistible temptation to put them in outside any version block. I also like to have all the enums for one platform in one list, not broken up by other stuff for other platforms, because then it is easier to verify against the C headers.
 Besides, without expression-level version(), the project's author might just 
 as likely think "Shoot, this is stupid that D doesn't let me make this 
 nicely DRY. But I really like DRY, so I guess I'll just resort to an 
 external C-like pre-processor".

There's something wrong with that option, because although it's always been on the table (not just for D, but for any language) nobody ever uses it. Back in the '80s people would often complain about the limitations of the C preprocessor, and the answer was always "if you want a real macro preprocessor, use m4". But I've never heard of anyone ever actually using m4 to preprocess C. The "just use m4" meme became a joke, like Bob Villa saying "just use track lighting".
Feb 10 2009
parent Sergey Gromov <snake.scaly gmail.com> writes:
Tue, 10 Feb 2009 19:57:47 -0800, Walter Bright wrote:

 Nick Sabalausky wrote:
 I have to admit, that's a very compelling example. I hadn't thought of 
 anything like that.

I didn't either, until I got bit by it <g>.
 I guess the moral is "DRY is normally great, but be very 
 careful with it when using it across multiple builds." But I'm still not 
 sure we should be going that far. For instance:
 
 enum
 {
     version(Linux || OSX)
     {
         O_RDONLY = 0,
         O_WRONLY = 1,
         O_RDWR = 2,
         O_CREAT = 0100,
     }
     version(Linux)
     {
         O_APPEND = 02000,
     }
     version(OSX)
     {
         O_APPEND = 8,
         O_SYMLINK = 0x200000,
     }
 }
 
 That seems reasonable to me.

Yeah, but I guarantee you that there will be some "common to all" ones and an irresistible temptation to put them in outside any version block. I also like to have all the enums for one platform in one list, not broken up by other stuff for other platforms, because then it is easier to verify against the C headers.
 Besides, without expression-level version(), the project's author might just 
 as likely think "Shoot, this is stupid that D doesn't let me make this 
 nicely DRY. But I really like DRY, so I guess I'll just resort to an 
 external C-like pre-processor".

There's something wrong with that option, because although it's always been on the table (not just for D, but for any language) nobody ever uses it.

There's Antenna for Java. It's nearly impossible to do without some preprocessor in Java if you want some mid-level library to work reliably on many different mobile phones. Compile once, run anywhere--sounds like a joke to me.
Feb 15 2009
prev sibling next sibling parent BCS <none anon.com> writes:
Hello Walter,

 and it works great for linux. Now I port my code to OSX, and it
 mysteriously dies. After much frustration, I discover that O_APPEND
 for OSX is 8, not 02000. (Yes, this kind of thing has happened to me.
 I only found it by suspecting the problem, and then going through
 every fscking definition comparing it to the one in the system .h
 file. Yeech.)

A little fun with a C compiler/preprocessor and you could make a C file that gives you the value by name. Then a bit of fun with D unittest that call into the C code and you could have a system to check if something got messed up for the next OS you port to.
Feb 10 2009
prev sibling next sibling parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter Bright wrote:

 Now let's try the other way. I add O_APPEND to the linux branch, and 
 linux works great. Now I port to OSX, and the compiler dies with 
 "O_APPEND is undefined". I know immediately exactly what is wrong, look 
 up the .h file, and insert the right O_APPEND into the OSX version of 
 the declaration.

Maybe I'm missing something here, but why is Mac OS X including std.c.linux.linux ? Shouldn't it use std.c.darwin.darwin instead ?
 Furthermore, when I build a FreeBSD version, the compiler bings at me 
 for every declaration that needs some porting attention, instead of 
 silently using the wrong values.

GDC got tired of porting std.c.linux.linux over, so it generates a std.c.unix.unix module from the C headers at configure time instead. Also helps with the constants that have different values for different architectures, in addition to having different values for different OS.
 This would be even better if the OSX and linux declarations were split 
 into separate "personality" modules. That way you can develop happily on 
 OSX without fear of accidentally breaking linux support. You can defer 
 dealing with the linux version until you actually on the linux machine 
 and are in an efficient position to take care of it.

The version(linux) and version(darwin) should keep those separated, right ? And save you when you include the wrong std.c module, too... --anders
Feb 11 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Anders F Björklund wrote:
 Walter Bright wrote:
 
 Now let's try the other way. I add O_APPEND to the linux branch, and 
 linux works great. Now I port to OSX, and the compiler dies with 
 "O_APPEND is undefined". I know immediately exactly what is wrong, 
 look up the .h file, and insert the right O_APPEND into the OSX 
 version of the declaration.

Maybe I'm missing something here, but why is Mac OS X including std.c.linux.linux ? Shouldn't it use std.c.darwin.darwin instead ?

You're not missing anything. This needs to be cleaned up. (BTW, it will be osx, not darwin, as "darwin" seems to be only used in internal Apple documentation, whereas the published stuff says "OSX".)
 Furthermore, when I build a FreeBSD version, the compiler bings at me 
 for every declaration that needs some porting attention, instead of 
 silently using the wrong values.

GDC got tired of porting std.c.linux.linux over, so it generates a std.c.unix.unix module from the C headers at configure time instead. Also helps with the constants that have different values for different architectures, in addition to having different values for different OS.

Doing it automatically is better, of course.
Feb 11 2009
parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter Bright skrev:

 Maybe I'm missing something here, but why is Mac OS X including
 std.c.linux.linux ? Shouldn't it use std.c.darwin.darwin instead ?

You're not missing anything. This needs to be cleaned up. (BTW, it will be osx, not darwin, as "darwin" seems to be only used in internal Apple documentation, whereas the published stuff says "OSX".)

"darwin" is the uname(1) name of Mac OS X, sort of like the "linux" for GNU/Linux. Darwin is also a stand-alone OS, but that isn't used much anymore (but still available from Apple as a CD download, or from the http://puredarwin.org project). OSX is the new name for Apple's OSes: Mac OS X and iPhone OS. And of course DMD can use anything, but GDC uses darwin/Unix. --anders PS. I personally think it looks bad, but it is slightly better than OS/X at least (which makes you think of OS/2 Warp)
Feb 11 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Anders F Björklund wrote:
 "darwin" is the uname(1) name of Mac OS X, sort of like the
 "linux" for GNU/Linux. Darwin is also a stand-alone OS, but
 that isn't used much anymore (but still available from Apple
 as a CD download, or from the http://puredarwin.org project).
 
 OSX is the new name for Apple's OSes: Mac OS X and iPhone OS.
 
 And of course DMD can use anything, but GDC uses darwin/Unix.

Apple can't seem to make up their mind. uname does return "Darwin". gcc predefines "__MACH__" and "__APPLE__", but neither darwin nor osx. The documentation all says osx.
 
 --anders
 
 
 PS. I personally think it looks bad, but it is slightly better
     than OS/X at least (which makes you think of OS/2 Warp)

OS/2 was the dumbest name for an operating system, because it is not an identifier so everyone invents a different identifier for it. I used to work for Data I/O. Try spelling that over the phone to someone.
Feb 11 2009
parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter Bright wrote:

 OSX is the new name for Apple's OSes: Mac OS X and iPhone OS.

 And of course DMD can use anything, but GDC uses darwin/Unix.

Apple can't seem to make up their mind. uname does return "Darwin". gcc predefines "__MACH__" and "__APPLE__", but neither darwin nor osx. The documentation all says osx.

Will you set version(darwin) though, or would that be "version(osx)" ? The existing D code for Mac OS X all uses version(darwin) from before. --anders
Feb 11 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Anders F Björklund wrote:
 Walter Bright wrote:
 
 OSX is the new name for Apple's OSes: Mac OS X and iPhone OS.

 And of course DMD can use anything, but GDC uses darwin/Unix.

Apple can't seem to make up their mind. uname does return "Darwin". gcc predefines "__MACH__" and "__APPLE__", but neither darwin nor osx. The documentation all says osx.

Will you set version(darwin) though, or would that be "version(osx)" ? The existing D code for Mac OS X all uses version(darwin) from before.

version(OSX) I just don't think darwin is a good idea, as Apple doesn't even use it. See http://developer.apple.com/unix/index.html, where "darwin" is clearly second string. It looks like Apple is pushing aside "darwin" in favor of "OSX". Fortunately, (darwin) => (OSX) is a trivial global search/replace.
Feb 12 2009
next sibling parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter Bright wrote:

 Fortunately, (darwin) => (OSX) is a trivial global search/replace.

That breaks GDC and LDC compatibility, unfortunately... So it'll be something like the Unix vs. Posix sitation. (where GDC pre-defines Unix and LDC pre-defines Posix) Maybe version(DigitalMars) version(OSX) version=darwin; --anders
Feb 12 2009
prev sibling next sibling parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter Bright wrote:

 I just don't think darwin is a good idea, as Apple doesn't even use it. 
 See http://developer.apple.com/unix/index.html, where "darwin" is 
 clearly second string. It looks like Apple is pushing aside "darwin" in 
 favor of "OSX".

GDC uses the same GNU triplets as GCC and configure, with the "os" part. (there's normally a cpu part, a vendor part, and a gnu part for linux) So the version used would be similar to the powerpc-apple-darwin8 or i686-apple-darwin9, or any of the other common variants on Mac OS X ? Like powerpc-unknown-linux-gnu and i686-pc-linux-gnu for Linux, it is usually the same as the uname(1) but in lower case (see config.guess) http://cvs.savannah.gnu.org/viewvc/*checkout*/config/config/config.guess These GNU triplets are also used when cross-compiling, for instance... They're not identical to the GDC versions, but I prefer using "darwin". The other common version(Unix) ones were: linux, darwin, cygwin, freebsd, solaris --anders PS. uname(1) on cygwin is somewhat weird, like "CYGWIN_NT-5.1" or so. And I think Solaris still returns "SunOS" for the system name...
Feb 12 2009
prev sibling next sibling parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter Bright wrote:

 version(OSX)

 I just don't think darwin is a good idea, as Apple doesn't even use it. 
 See http://developer.apple.com/unix/index.html, where "darwin" is 
 clearly second string. It looks like Apple is pushing aside "darwin" in 
 favor of "OSX".

Once upon a time, there was a difference between Darwin and Mac OS X. Where Darwin was the underlying (open source) operating system, but excluding the (proprietary) frameworks such as Carbon and Cocoa... So you had two: Darwin OS and Mac OS X. And both of them were "Darwin", so the term "puredarwin" was being used to refer to the open source OS. If you only target the vendor product, and not the open source one, then it doesn't really matter whether "Darwin" or "MacOSX" is used. (or "OSX", which includes both iPhone OS and Mac OS X in one term) Like you mentioned, GCC only predefines __APPLE__ and __MACH__... Now, I still hate the term "OSX" but that's without technical reasons. --anders PS. See also http://en.wikipedia.org/wiki/Darwin_(operating_system)
Feb 12 2009
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
Walter Bright wrote:
 Anders F Björklund wrote:
 Walter Bright wrote:

 OSX is the new name for Apple's OSes: Mac OS X and iPhone OS.

 And of course DMD can use anything, but GDC uses darwin/Unix.

Apple can't seem to make up their mind. uname does return "Darwin". gcc predefines "__MACH__" and "__APPLE__", but neither darwin nor osx. The documentation all says osx.

Will you set version(darwin) though, or would that be "version(osx)" ? The existing D code for Mac OS X all uses version(darwin) from before.

version(OSX) I just don't think darwin is a good idea, as Apple doesn't even use it. See http://developer.apple.com/unix/index.html, where "darwin" is clearly second string. It looks like Apple is pushing aside "darwin" in favor of "OSX".

I'm not going to argue about what is the best version identifier for Mac OS X, the I only thing I care about is consistency and "darwin" is already used by LDC and GDC. Don't make things harder.
 Fortunately, (darwin) => (OSX) is a trivial global search/replace.

And I assume it's equally trivial to implement "darwin" as the version identifier. Again, don't make things harder by breaking existing code.
Feb 13 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Jacob Carlborg" <doob me.com> wrote in message 
news:gn49b6$49b$1 digitalmars.com...
 Walter Bright wrote:
 Anders F Björklund wrote:
 Walter Bright wrote:

 OSX is the new name for Apple's OSes: Mac OS X and iPhone OS.

 And of course DMD can use anything, but GDC uses darwin/Unix.

Apple can't seem to make up their mind. uname does return "Darwin". gcc predefines "__MACH__" and "__APPLE__", but neither darwin nor osx. The documentation all says osx.

Will you set version(darwin) though, or would that be "version(osx)" ? The existing D code for Mac OS X all uses version(darwin) from before.

version(OSX) I just don't think darwin is a good idea, as Apple doesn't even use it. See http://developer.apple.com/unix/index.html, where "darwin" is clearly second string. It looks like Apple is pushing aside "darwin" in favor of "OSX".

I'm not going to argue about what is the best version identifier for Mac OS X, the I only thing I care about is consistency and "darwin" is already used by LDC and GDC. Don't make things harder.
 Fortunately, (darwin) => (OSX) is a trivial global search/replace.

And I assume it's equally trivial to implement "darwin" as the version identifier. Again, don't make things harder by breaking existing code.

I don't have an opinion on the correct identifier to use, but not implementing a change that makes something more correct *just* because it breaks existing code (particularly in an easy-to-fix way) is a terrible strategy. That's exactly what's turned C++ into the abysmal mess that it is today, which in turn is one of the primary reasons for D's existence. I don't want to end up with yet another C++.
Feb 13 2009
parent reply Jacob Carlborg <doob me.com> writes:
Nick Sabalausky wrote:
 "Jacob Carlborg" <doob me.com> wrote in message 
 news:gn49b6$49b$1 digitalmars.com...
 Walter Bright wrote:
 Anders F Björklund wrote:
 Walter Bright wrote:

 OSX is the new name for Apple's OSes: Mac OS X and iPhone OS.

 And of course DMD can use anything, but GDC uses darwin/Unix.

predefines "__MACH__" and "__APPLE__", but neither darwin nor osx. The documentation all says osx.

The existing D code for Mac OS X all uses version(darwin) from before.

I just don't think darwin is a good idea, as Apple doesn't even use it. See http://developer.apple.com/unix/index.html, where "darwin" is clearly second string. It looks like Apple is pushing aside "darwin" in favor of "OSX".

OS X, the I only thing I care about is consistency and "darwin" is already used by LDC and GDC. Don't make things harder.
 Fortunately, (darwin) => (OSX) is a trivial global search/replace.

identifier. Again, don't make things harder by breaking existing code.

I don't have an opinion on the correct identifier to use, but not implementing a change that makes something more correct *just* because it breaks existing code (particularly in an easy-to-fix way) is a terrible strategy. That's exactly what's turned C++ into the abysmal mess that it is today, which in turn is one of the primary reasons for D's existence. I don't want to end up with yet another C++.

I guess I have to start to argue. In this case I don't see that some version identifier is more correct than the other, you just have to make a choice. Either you choose to be as close to the official name of the operating system as possible, in this case I guess it should be "Mac OS X". Or you choose the uname that I've seen some people here talk about or a part of the target triple or what it's called, for example i386-apple-darwin. Then there's the question about upper/lowercase and spaces in the name. Or any other suggestions. I have know idea what the initial thought was when Walter chose the version identifiers and therefore I have no idea what would be the most correct version identifier to choose in this case. Because of gdc and ldc already use "darwin" you have to make a special case for dmd. Of course they could change the version identifier but at least for gdc I don't see that's very likely. GDC seems quite dead but it's still the most reliable compiler for Mac OS X at this point. I have no problem using dmd when it becomes equally reliable on Mac OS X as on linux/windows but as far as I know it will only be for x86 and there are three other targets for osx (excluding iphone): x86-64, ppc and ppc64. So what is the most correct version identifier? I don't know. But I think you should choose one option form the beginning and then try to be as consistent as possible. I guess I have to add this to every dsss file: version (DigitalMars) version (OSX) version = darwin /Jacob Carlborg
Feb 14 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Jacob Carlborg" <doob me.com> wrote in message 
news:gn693c$14de$1 digitalmars.com...
 Nick Sabalausky wrote:
 "Jacob Carlborg" <doob me.com> wrote in message 
 news:gn49b6$49b$1 digitalmars.com...
 Walter Bright wrote:
 Anders F Björklund wrote:
 Walter Bright wrote:

 OSX is the new name for Apple's OSes: Mac OS X and iPhone OS.

 And of course DMD can use anything, but GDC uses darwin/Unix.

gcc predefines "__MACH__" and "__APPLE__", but neither darwin nor osx. The documentation all says osx.

The existing D code for Mac OS X all uses version(darwin) from before.

I just don't think darwin is a good idea, as Apple doesn't even use it. See http://developer.apple.com/unix/index.html, where "darwin" is clearly second string. It looks like Apple is pushing aside "darwin" in favor of "OSX".

OS X, the I only thing I care about is consistency and "darwin" is already used by LDC and GDC. Don't make things harder.
 Fortunately, (darwin) => (OSX) is a trivial global search/replace.

identifier. Again, don't make things harder by breaking existing code.

I don't have an opinion on the correct identifier to use, but not implementing a change that makes something more correct *just* because it breaks existing code (particularly in an easy-to-fix way) is a terrible strategy. That's exactly what's turned C++ into the abysmal mess that it is today, which in turn is one of the primary reasons for D's existence. I don't want to end up with yet another C++.

I guess I have to start to argue. In this case I don't see that some version identifier is more correct than the other, you just have to make a choice. Either you choose to be as close to the official name of the operating system as possible, in this case I guess it should be "Mac OS X". Or you choose the uname that I've seen some people here talk about or a part of the target triple or what it's called, for example i386-apple-darwin. Then there's the question about upper/lowercase and spaces in the name. Or any other suggestions. I have know idea what the initial thought was when Walter chose the version identifiers and therefore I have no idea what would be the most correct version identifier to choose in this case. Because of gdc and ldc already use "darwin" you have to make a special case for dmd. Of course they could change the version identifier but at least for gdc I don't see that's very likely. GDC seems quite dead but it's still the most reliable compiler for Mac OS X at this point. I have no problem using dmd when it becomes equally reliable on Mac OS X as on linux/windows but as far as I know it will only be for x86 and there are three other targets for osx (excluding iphone): x86-64, ppc and ppc64. So what is the most correct version identifier? I don't know. But I think you should choose one option form the beginning and then try to be as consistent as possible. I guess I have to add this to every dsss file: version (DigitalMars) version (OSX) version = darwin

I see what you're saying. In that case, perhaps the best thing to do is, for now, keep it as something that's consistent across all D compilers, and then once GDC is finally made *completely* obsolete by combined effort of LDC and DMD (or revived by someone brave enough to dig into the gcc code), then we could make it whatever seems to be most accurate.
Feb 14 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Nick Sabalausky wrote:
 "Jacob Carlborg"<doob me.com>  wrote in message
 news:gn693c$14de$1 digitalmars.com...
 Nick Sabalausky wrote:
 "Jacob Carlborg"<doob me.com>  wrote in message
 news:gn49b6$49b$1 digitalmars.com...
 Walter Bright wrote:
 Anders F Björklund wrote:
 Walter Bright wrote:

 OSX is the new name for Apple's OSes: Mac OS X and iPhone OS.

 And of course DMD can use anything, but GDC uses darwin/Unix.

gcc predefines "__MACH__" and "__APPLE__", but neither darwin nor osx. The documentation all says osx.

The existing D code for Mac OS X all uses version(darwin) from before.

I just don't think darwin is a good idea, as Apple doesn't even use it. See http://developer.apple.com/unix/index.html, where "darwin" is clearly second string. It looks like Apple is pushing aside "darwin" in favor of "OSX".

OS X, the I only thing I care about is consistency and "darwin" is already used by LDC and GDC. Don't make things harder.
 Fortunately, (darwin) =>  (OSX) is a trivial global search/replace.

identifier. Again, don't make things harder by breaking existing code.

implementing a change that makes something more correct *just* because it breaks existing code (particularly in an easy-to-fix way) is a terrible strategy. That's exactly what's turned C++ into the abysmal mess that it is today, which in turn is one of the primary reasons for D's existence. I don't want to end up with yet another C++.

version identifier is more correct than the other, you just have to make a choice. Either you choose to be as close to the official name of the operating system as possible, in this case I guess it should be "Mac OS X". Or you choose the uname that I've seen some people here talk about or a part of the target triple or what it's called, for example i386-apple-darwin. Then there's the question about upper/lowercase and spaces in the name. Or any other suggestions. I have know idea what the initial thought was when Walter chose the version identifiers and therefore I have no idea what would be the most correct version identifier to choose in this case. Because of gdc and ldc already use "darwin" you have to make a special case for dmd. Of course they could change the version identifier but at least for gdc I don't see that's very likely. GDC seems quite dead but it's still the most reliable compiler for Mac OS X at this point. I have no problem using dmd when it becomes equally reliable on Mac OS X as on linux/windows but as far as I know it will only be for x86 and there are three other targets for osx (excluding iphone): x86-64, ppc and ppc64. So what is the most correct version identifier? I don't know. But I think you should choose one option form the beginning and then try to be as consistent as possible. I guess I have to add this to every dsss file: version (DigitalMars) version (OSX) version = darwin

I see what you're saying. In that case, perhaps the best thing to do is, for now, keep it as something that's consistent across all D compilers, and then once GDC is finally made *completely* obsolete by combined effort of LDC and DMD (or revived by someone brave enough to dig into the gcc code), then we could make it whatever seems to be most accurate.

(i.e darwin plus apple's proprietery APIs like cocoa, carbon). if that's true isn't it more acurate to have version darwin for the darwin OS (someone mentioned opendarwin), and osx version for Apple's Mac OS X in addition to darwin version? i.e Mac OS X is both "osx" and "darwin" whereas opendarwin is only "darwin". does that make sense?
Feb 14 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Yigal Chripun" <yigal100 gmail.com> wrote in message 
news:gn76f3$big$1 digitalmars.com...
 I'm no Mac user but it seems to me that OS X is a superset of darwin (i.e 
 darwin plus apple's proprietery APIs like cocoa, carbon).
 if that's true isn't it more acurate to have version darwin for the darwin 
 OS (someone mentioned opendarwin), and osx version for Apple's Mac OS X in 
 addition to darwin version? i.e Mac OS X is both "osx" and "darwin" 
 whereas opendarwin is only "darwin".
 does that make sense?

*Seems* to make sense to me, but then again, I'm not a mac user either.
Feb 14 2009
prev sibling parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Yigal Chripun wrote:

 I'm no Mac user but it seems to me that OS X is a superset of darwin 
 (i.e darwin plus apple's proprietery APIs like cocoa, carbon).
 if that's true isn't it more acurate to have version darwin for the 
 darwin OS (someone mentioned opendarwin), and osx version for Apple's 
 Mac OS X in addition to darwin version? i.e Mac OS X is both "osx" and 
 "darwin" whereas opendarwin is only "darwin".
 does that make sense?

Sortof, just that OpenDarwin/PureDarwin are normally "puredarwin" while Mac OS X is "macosx" - both in addition to regular "darwin". At least those are the "variants" that DarwinPorts/MacPorts uses, Apple recommends to check for specific functionality (Carbon/Cocoa) using something like autoconf and not assume things from the OS name. But D could still do user-level versions of those features, though ? In MacPorts, the toggle for +puredarwin/+macosx is a simple Tcl check: [file isdirectory /System/Library/Frameworks/Carbon.framework] But yeah, it would be nice if DMD could set *both* "version(darwin)" and "version(OSX)" for Mac OS X. That way the old code would work too. There would still be some library issues between Phobos and gPhobos, like std.c.osx.osx verus std.c.unix.unix, but that's another story... --anders
Feb 14 2009
next sibling parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
 But yeah, it would be nice if DMD could set *both* "version(darwin)"
 and "version(OSX)" for Mac OS X. That way the old code would work too.

... and it did. :-) --anders
Feb 14 2009
prev sibling parent Christian Kamm <kamm-incasoftware removethis.de> writes:
Anders F Björklund wrote:
 But yeah, it would be nice if DMD could set both "version(darwin)"
 and "version(OSX)" for Mac OS X. That way the old code would work too.

LDC will also add the OSX version specifier for Mac OS X. Christian
Feb 15 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Walter Bright, el 10 de febrero a las 17:16 me escribiste:
Duplicating (nearly all of a) source file is NOT, repeat NOT, a
satisfatory solution.

problem, but not a huge one. I find that the meld utility (on linux) makes this chore a snap.

tools to get me out of this problem too.

meld is particularly nice. Andrei showed it to me: http://www.linux.com/feature/61372

You should probably distribute meld with DMD then ;) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Hey you, out there on your own Sitting naked by the phone Would you touch me?
Feb 11 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Leandro Lucarella wrote:
 You should probably distribute meld with DMD then ;)

sudo apt-get install meld isn't that hard <g>.
Feb 11 2009
parent reply BCS <ao pathlink.com> writes:
Reply to Walter,

 Leandro Lucarella wrote:
 
 You should probably distribute meld with DMD then ;)
 

isn't that hard <g>.

doesn't work unless you have apt-get working, doesn't work unless you have a net connection, doesn't work if you don't have root... OTOH a tarball doesn't work on any other platform, but then again nether does DMD.
Feb 11 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
BCS wrote:
 Reply to Walter,
 
 Leandro Lucarella wrote:

 You should probably distribute meld with DMD then ;)

isn't that hard <g>.

doesn't work unless you have apt-get working, doesn't work unless you have a net connection, doesn't work if you don't have root... OTOH a tarball doesn't work on any other platform, but then again nether does DMD.

You can't download dmd.zip without a net connection, either!
Feb 11 2009
parent reply BCS <none anon.com> writes:
Hello Walter,

 BCS wrote:
 
 Reply to Walter,
 
 Leandro Lucarella wrote:
 
 You should probably distribute meld with DMD then ;)
 

isn't that hard <g>.

have a net connection, doesn't work if you don't have root... OTOH a tarball doesn't work on any other platform, but then again nether does DMD.


last I checked you cant use apt-get over sneeker net.
Feb 11 2009
next sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
BCS wrote:
 Hello Walter,
 
 BCS wrote:

 Reply to Walter,

 Leandro Lucarella wrote:

 You should probably distribute meld with DMD then ;)

isn't that hard <g>.

have a net connection, doesn't work if you don't have root... OTOH a tarball doesn't work on any other platform, but then again nether does DMD.


last I checked you cant use apt-get over sneeker net.

I believe 'apt-get install' actually will work so long as the sources/.deb are in the right location. That said, I haven't ever tried it. ;) If nothing else, you should be able to use dpkg. Lucky Gentoo users can use emerge directly. -- Chris Nicholson-Sauls
Feb 11 2009
prev sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
BCS wrote:
 Hello Walter,
 
 BCS wrote:

 Reply to Walter,

 Leandro Lucarella wrote:

 You should probably distribute meld with DMD then ;)

isn't that hard <g>.

have a net connection, doesn't work if you don't have root... OTOH a tarball doesn't work on any other platform, but then again nether does DMD.


last I checked you cant use apt-get over sneeker net.

`wget $URL_OF_PACKAGE` to download it, and (I believe; it's been a while since I did this...) `dpkg -i $PACKAGE_FILE` to install it. That, or you can apt-get on one machine, look at what it downloads and installs, and copy the cached .debs to another machine. :) -- Daniel
Feb 11 2009
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 bobef wrote:
 I was thinking... what is the point of version() ? It is so
 inflexible. There is no even version(!...). Why not "static
 if(version(DMD))" or static if(is(version == DMD))?

The version statement is designed to be inflexible. It's designed to encourage abstracting and modularizing version differences, rather than encouraging line-by-line differences. I've used #if/#endif for decades. Over time, it *always* degenerates into an utter rat's nest of nested, incomprehensible complexity (and this includes my own code). For a lovely example of how bad it can get, take a look at the source code to Hans Boehm's garbage collector. I defy anyone to figure it out without running the code through the preprocessor first to see which statements are actually getting compiled.

There can exist a happy medium between the rigid current design and the disaster that is #if.
 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. 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. 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! Add to this nightmare that I have to worry about syntax errors, or doing stupid shit like: version(BorA) // whoops, it's spelled AorB stupid! no compiler error given All this can be reduced to 0 statements, and 0 headache if the logical || can be used. This is why we have computers, so we don't have to calculate this stuff. Please don't make me use my brain and typing skills where a computer is soooo much better.
 3. version (A && B) can be done as:

    version (A) version (B) { ... }

This isn't as bad of a problem as ||, but still could be nicer as the suggested && syntax. But I'd be ok with having just the ||.
 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. -Steve
Feb 10 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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.
Feb 10 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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.)
Feb 10 2009
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"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...
Feb 10 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 "Walter Bright" <newshound1 digitalmars.com> wrote in message 
 news:gmt6l0$rff$1 digitalmars.com...
 Denis Koroskin wrote:
 Does it look any better? No way!

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.

But they do make it more painful to write the rat's nest, which can be motivating to find a more appropriate solution.
 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...

When cookies and veggies are laid out on the buffet, I tend to reach for the cookies <g>.
Feb 10 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Nick Sabalausky wrote:
 "Walter Bright" <newshound1 digitalmars.com> wrote in message 
 news:gmt6l0$rff$1 digitalmars.com...
 Denis Koroskin wrote:
 Does it look any better? No way!

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.

But they do make it more painful to write the rat's nest, which can be motivating to find a more appropriate solution.

And when the rats nest MUST be created? Why make it more painful? I'm sure if Hans Bohem could write it in a clearer fashion he would have. To be perfectly clear, I absolutely agree with version statements not messing with the separation between semantic analysis and parsing, but these shortcuts for having multiple version identifiers in one statement do not hurt anything. I contend it is the lack of separation in C's preprocessor that makes it difficult to understand, not the lack of requirements on the #if itself. At least, that's what I've always hated about C/C++ preprocessor. The same arguments could be said about actual if statements in code, yet you have no qualms not forcing people to combine their logic into boolean variables before using an if statement.
 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...

When cookies and veggies are laid out on the buffet, I tend to reach for the cookies <g>.

It's more like moldy cookies and half-eaten donuts :) Neither looks appetizing, but when your really hungry... -Steve
Feb 10 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 And when the rats nest MUST be created?  Why make it more painful?  I'm sure 
 if Hans Bohem could write it in a clearer fashion he would have.

Hans is in a trap with it. If he reengineered it, he very likely would break many platforms, and he does not have those platforms and has no way to test. The code is very brittle, and it would take a lot of guts to try and reorganize it. The hbgc is a collection of code submitted by many, many people.
 The same arguments could be said about actual if statements in code, yet you 
 have no qualms not forcing people to combine their logic into boolean 
 variables before using an if statement.

It's not the same, because versions tend to slice through whole programs in an unstructured manner, while garden variety if statements are structured and local.
Feb 10 2009
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 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

All you have done is split the mess into separate files. This does not solve the problem.
 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.).

But you have, because inadvertently, you changed some code in the actual implementation to use the new identifiers you made in your special new file. Now you have to go back and rethink the sunos include because you broke it. Mess still exists. (of course, I have no idea, but I gave you as much of an example/proof as you did ;)
 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.

That I would agree, and it turns out your example suffers from the same issues, i.e. #if __STDC__ -Steve
Feb 10 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 "Walter Bright" wrote
 Denis Koroskin wrote:
 Does it look any better? No way!

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

All you have done is split the mess into separate files. This does not solve the problem.

How does it not solve the problem?
 
 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.).

But you have, because inadvertently, you changed some code in the actual implementation to use the new identifiers you made in your special new file. Now you have to go back and rethink the sunos include because you broke it. Mess still exists. (of course, I have no idea, but I gave you as much of an example/proof as you did ;)

See my example of O_APPEND for the proof that it does solve the problem without breaking other platforms.
Feb 10 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright"  wrote
 Steven Schveighoffer wrote:
 "Walter Bright" wrote
 Denis Koroskin wrote:
 Does it look any better? No way!

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

All you have done is split the mess into separate files. This does not solve the problem.

How does it not solve the problem?

I assume that the rats nest is necessary for the code to function. Splitting up the rats nest into either a) mostly duplicated rats nests, or b) individual chunks of rats nest still constitutes a rat's nest.
 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.).

But you have, because inadvertently, you changed some code in the actual implementation to use the new identifiers you made in your special new file. Now you have to go back and rethink the sunos include because you broke it. Mess still exists. (of course, I have no idea, but I gave you as much of an example/proof as you did ;)

See my example of O_APPEND for the proof that it does solve the problem without breaking other platforms.

That example has nothing to do with || being allowed, and nothing to do with your exhibit B. Your simple example makes sense to split into multiple files, but you simply waved your magic "split into multiple files" wand at Hans' code and because of the simplicity of D's versioning system, it's more maintainable? I find it hard to believe without proof. Besides, your example exists *without* any new features to the versioning system. That is, your example just shows how it's easier to maintain if you put the version identifier at the outermost layer, instead of in the innermost layer, and I agree with you there. I just want to try and get rid of the cruft of: version(A) version = AorB; version(B) version = AorB; If you want to prove how much easier it is, actually split Hans' rat's nest into multiple files how you would do it, and I'll show you that it isn't any harder to maintain if you use ||. I think Nick already showed how much more readable it is in one file (although he did also use ! in addition to || and &&). -Steve
Feb 10 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 That example has nothing to do with || being allowed, and nothing to do with 
 your exhibit B.  Your simple example makes sense to split into multiple 
 files, but you simply waved your magic "split into multiple files" wand at 
 Hans' code and because of the simplicity of D's versioning system, it's more 
 maintainable?  I find it hard to believe without proof.

I cannot prove to you in any mathematical sort of way. All I can say is that I have a lot of experience doing things both ways, and what the results are. I can readily believe that it isn't obvious until one has done it both ways; programming is full of examples of such. Such as: #define BEGIN { #define END } I can't prove that's a bad idea, either. But experience shows it is <g>.
 Besides, your example exists *without* any new features to the versioning 
 system.  That is, your example just shows how it's easier to maintain if you 
 put the version identifier at the outermost layer, instead of in the 
 innermost layer, and I agree with you there.  I just want to try and get rid 
 of the cruft of:
 
 version(A)
    version = AorB;
 version(B)
    version = AorB;

I think I've been misunderstood here. I never intended one to actually use the "or" string in a version, my intent was that one determine just what the feature is that is AorB, and name the identifier that. For example: version (linux) version = Pthreads; version (OSX) version = Pthreads; version (Pthreads) ... else version (Windows) ... I don't consider that cruft, but very reasonable self-documentation.
 If you want to prove how much easier it is, actually split Hans' rat's nest 
 into multiple files how you would do it, and I'll show you that it isn't any 
 harder to maintain if you use ||.

I already did port it once, many years ago, it's why I know how hard it is. Take a look at D's gc which uses personality modules. I ported that to the mac pretty easily and never touched the main code. I can't even figure out where the mac osx code is in the hbgc or what would need to be changed for it. I couldn't even figure out if the hbgc supported OSX or not. To sum up, I've done it both ways. The personality module method approach is head and shoulders the clear winner.
Feb 10 2009
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"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 ||): ===============================

 ===============================

 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.
Feb 10 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 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.

I'll argue that I've never seen anyone create such a mess in D, while I see it regularly in C. So something about D is discouraging developing those things. I think the tipping point is that it's too easy in C to slip into writing such a mess without actually trying to, while in D you have to work harder to do it. Hard enough that one might as well do it better in the first place.
Feb 10 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:gmtatl$12om$2 digitalmars.com...
 Nick Sabalausky wrote:
 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.

I'll argue that I've never seen anyone create such a mess in D, while I see it regularly in C.

Agreed, but I'm certain that a significant reason for that is the fact that (and I mean no disrespect to D by this) C has had far more people working on a far larger number of projects that each span far more platforms all for a far longer time. Just look at all of the platforms supported by that GC example. Even the D language itself isn't that multi-platform yet. And then there's all of that GNU C code that's been around much longer than even D itself. If D had the ubiquity of C (as I hope someday it will), then D would have plenty of mess too. Not as much, granted (for reasons I argue below), but still a fair amount.
 So something about D is discouraging developing those things.

 I think the tipping point is that it's too easy in C to slip into writing 
 such a mess without actually trying to, while in D you have to work harder 
 to do it.

I agree there are things about D that discourage such messy tactics, but I don't think it's the lack of !, ||, && and expression-level usage. I think, as Steven said, that the discouragement comes more from version() being a syntactic/semantic part of the language itself instead of a blunt text-replacement engine that neither knows nor cares what it tramples over. That's why you have to work harder to make a conditional-compilation mess, not because it lacks a few features. Plus, the C preprocessor's syntax automatically invokes a mental association with "blunt text-editing tool" and I think that's a big part of why C coders slip into the mess. D's version() and static if() *look* like an ordinary if(), so that alone is going to decrease urges to use them like a C preprocessor.
Feb 10 2009
prev sibling next sibling parent reply "Joel C. Salomon" <joelcsalomon gmail.com> writes:
Jarrett Billingsley wrote:
 If nothing - NOTHING - else, please give us version(!something).  Out
 of everything wrong with it, that is the worst.  You say that
 according to "cognitive studies" that people don't see the !; then why
 didn't you remove it from the rest of the language?  And how is THIS:
 
 version(Poopy) {} else
 {
 
 }
 
 supposedly better?!

version(!Windows) // some added functionality that depends on UNIX-like semantics Then port the code to something oddball—Plan 9, say, or VMS. —Joel Salomon
Feb 11 2009
parent reply "Joel C. Salomon" <joelcsalomon gmail.com> writes:
Jarrett Billingsley wrote:
 I use versions for things other than platform-specific code.  Like
 library compilation options.

So what would your use-case look like? —Joel Salomon
Feb 11 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jarrett Billingsley wrote:
 version(TurnOffSomeFeature) {} else
 {
     codeForFeature();
 }
 
 Sometimes you want to have things that turn _off_ code from a default
 of being on.
 
 I know, I know, Walter said that "[v]ersions should be positive
 things, because a version is something that is being build - one
 doesn't craft a makefile to build a notLinux."  But the fact is,
 platform-specific stuff and code options are two very different use
 cases.  Platform-specific things are completely invisible to the user
 of a library and only one configuration can ever be in use on a single
 platform.  Code options, on the other hand, are based on user
 preference.  Some things are extra, and it makes sense to have
 switches to turn them on.  But some things are the other way, where
 you want them *on* by default and want to have the option of turning
 them *off*.

Even if a feature is on by default, versions should still be a positive thing, otherwise you have a double negative: version (!TurnOffSomeFeature) Even in regular code, I try to avoid naming things that way, because double negatives are twice as hard to comprehend when things get complicated. I've gone through my code to remove such double negatives as much as possible. So I suggest: version (SomeFeature) { codeForFeature(); } is clearer, even if SomeFeature is the default.
Feb 11 2009
next sibling parent reply "Joel C. Salomon" <joelcsalomon gmail.com> writes:
Walter Bright wrote:
 So I suggest:
 
 version (SomeFeature)
 {
     codeForFeature();
 }
 
 is clearer, even if SomeFeature is the default.

Apologies if this is obvious, but: • How do you set SomeFeature as default? and • How do you declare the non-implementation of SomeFeature? —Joel Salomon
Feb 11 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Joel C. Salomon wrote:
 Walter Bright wrote:
 So I suggest:

 version (SomeFeature)
 {
     codeForFeature();
 }

 is clearer, even if SomeFeature is the default.

Apologies if this is obvious, but: • How do you set SomeFeature as default? and

Either via the command line, or by the statement: version = SomeFeature;
 • How do you declare the non-implementation of SomeFeature?

Don't declare the version SomeFeature.
Feb 11 2009
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Jarrett Billingsley wrote:
 It might be clearer in the code, but it's far less intuitive for the
 library user!

I don't see how.
 And if SomeFeature is _not_ defined by default, it's not a very good
 default, is it!

Define it by default in your makefile or by a version=SomeFeature; declaration.
Feb 11 2009
prev sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Jarrett Billingsley wrote:
 On Wed, Feb 11, 2009 at 4:11 PM, Bill Baxter <wbaxter gmail.com> wrote:
 I thought he meant this:

 // once at the top
 version(SomeFeatureDisabled) {
 } else { version = SomeFeature; }

 // in rest of code ...
 version (SomeFeature)
 {
    codeForFeature();
 }

I don't know whether or not Walter _was_ actually insinuating that but it's a pretty good idea ;) the only disadvantage of course being that I have to copy the //once at the top part into every file that wants to use the SomeFeature version, though that probably wouldn't/shouldn't be a big issue in practice.

I had the same thought at first when reading the 'version declarations' proposal elsewhere in this thread, and was going to put forth having to write 'version Tango = extern;' at the top of modules as an example of what could be bad about it... but then it occurred to me how often I've ever had to use version(Tango) in the first place. (Not very often at all.) It would be the sort of thing you use just here and there, and is actually self-documenting in the process, as it makes it clear that /this/ module is going to fork on certain versions. Despite reservations, its all growing on me. -- Chris Nicholson-Sauls
Feb 11 2009
prev sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Wed, Feb 11, 2009 at 4:11 PM, Bill Baxter <wbaxter gmail.com> wrote:
 I thought he meant this:

 // once at the top
 version(SomeFeatureDisabled) {
 } else { version = SomeFeature; }

 // in rest of code ...
 version (SomeFeature)
 {
    codeForFeature();
 }

I don't know whether or not Walter _was_ actually insinuating that but it's a pretty good idea ;) the only disadvantage of course being that I have to copy the //once at the top part into every file that wants to use the SomeFeature version, though that probably wouldn't/shouldn't be a big issue in practice.
Feb 11 2009
prev sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Thu, Feb 12, 2009 at 6:02 AM, Jarrett Billingsley
<jarrett.billingsley gmail.com> wrote:
 On Wed, Feb 11, 2009 at 2:57 PM, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Jarrett Billingsley wrote:
 So I suggest:

 version (SomeFeature)
 {
    codeForFeature();
 }

 is clearer, even if SomeFeature is the default.

It might be clearer in the code, but it's far less intuitive for the library user! And if SomeFeature is _not_ defined by default, it's not a very good default, is it!

I thought he meant this: // once at the top version(SomeFeatureDisabled) { } else { version = SomeFeature; } // in rest of code ... version (SomeFeature) { codeForFeature(); } --bb
Feb 11 2009
parent "Nick Sabalausky" <a a.a> writes:
"Bill Baxter" <wbaxter gmail.com> wrote in message 
news:mailman.689.1234386722.22690.digitalmars-d puremagic.com...
 // once at the top
 version(SomeFeatureDisabled) {
 } else { version = SomeFeature; }

It would be a hell of a lot nicer if we could just write that as: version SomeFeature = !SomeFeatureDisabled; or even better, something like: version SomeFeature.default = true;
Feb 11 2009
prev sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Wed, Feb 11, 2009 at 2:57 PM, Walter Bright
<newshound1 digitalmars.com> wrote:
 Jarrett Billingsley wrote:
 So I suggest:

 version (SomeFeature)
 {
    codeForFeature();
 }

 is clearer, even if SomeFeature is the default.

It might be clearer in the code, but it's far less intuitive for the library user! And if SomeFeature is _not_ defined by default, it's not a very good default, is it!
Feb 11 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Feb 10 2009
next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
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 }
Feb 10 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 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

Sometimes, coming up with a better identifier than this || that is not easy. For example, in Tango, there is a version for Posix, which is defined if linux or darwin or bsd is defined, but we sometimes get people arguing that Windows is posix compliant too! Not only that, but are you going to want to define a special identifier in the header file that is used exactly ONCE in your code? Often times, I see this in source files: version(linux) version = specialWay; version(darwin) version = specialWay; Because they need that combination of linux || darwin in one spot, but nowhere else. I see no reason why this is more readable than version(linux || darwin) in that one spot. I do not have to go looking for specialWay in this file, or some other header file, or simply have to read through useless repetitive statements. It's perfectly clear what it means. How does this promote confusing versioning? Many people have pointed out the possibility of spelling mistakes now more likely because you have to repetitively specify the same version under other version branches. I see allowing version(x || y) as something that has absolutely no drawbacks. None of the arguments against it have had any merit, because the examples of how bad it would be are not bad because of the existance of that construct. They are simply complicated examples of versioned code, which would probably benefit from allowing ||. It would even make code more maintainable in your example: version(A || C) version = canBeUsedToPickUpChicks; Note no duplication of the long token you chose ;) -Steve
Feb 10 2009
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
Andrei Alexandrescu wrote:
 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

There's one slightly irritating issue I've run into with versions recently. In the spare few instances where I actually need to do something like this, I like to put everything together so I know at a glance what's going on. However, versions may not be defined in class scope, so: class C { version (A) version = useSomeFancyThing; version (B) version = useSomeFancyThing; version (useSomeFancyThing) { Fancy[1024] fancyPants; } } This is illegal. Kind of a small issue, but I grumbled about it for a minute or two before I broken down and just duplicated the declaration for each version. I could have easily put the tests at module scope before the class declaration, but I wanted everything together. Sean
Feb 10 2009
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:gmsqnh$6qi$1 digitalmars.com...
 The version statement is designed to be inflexible. It's designed to 
 encourage abstracting and modularizing version differences, rather than 
 encouraging line-by-line differences.

 I've used #if/#endif for decades. Over time, it *always* degenerates into 
 an utter rat's nest of nested, incomprehensible complexity (and this 
 includes my own code). For a lovely example of how bad it can get, take a 
 look at the source code to Hans Boehm's garbage collector. I defy anyone 
 to figure it out without running the code through the preprocessor first 
 to see which statements are actually getting compiled.

 A far better way to do versioning is to find the abstraction lines 
 separating versions, and put version specific code in separate modules 
 that are then imported. It makes for nice, clean, understandable code.

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.
 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)".
 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". 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". Also, you didn't respond to the concerns about typos in a version identifier. Can we assume you agree that's a problem?
Feb 10 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
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.
Feb 10 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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>.
Feb 10 2009
parent reply Don <nospam nospam.com> writes:
Walter Bright wrote:
 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>.

I've got two words for you, Walter. Compiler bugs.
Feb 10 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don wrote:
 Walter Bright wrote:
 This is to combat the pernicious problem of having features that 
 subtract code <g>.

I've got two words for you, Walter. Compiler bugs.

I've never heard of those before. Are they like VW Bugs?
Feb 10 2009
parent Don <nospam nospam.com> writes:
Walter Bright wrote:
 Don wrote:
 Walter Bright wrote:
 This is to combat the pernicious problem of having features that 
 subtract code <g>.

I've got two words for you, Walter. Compiler bugs.

I've never heard of those before. Are they like VW Bugs?

Yes, but they're not as cute.
Feb 11 2009
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:gmt1s1$i7f$1 digitalmars.com...
 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."

It seems to me that D's versioning, conditional compilation, and overall feature set are already different enough that people aren't necessarily going to be falling into the "c preprocessor style" rut. Plus, as someone else mentioned, there *are* still concerns about code duplication. So it really becomes a balancing act between clear abstraction and DRY. D's version() just makes this balancing act harder because it keeps pushing in the one direction. I'll grant that if there's a bad habit that most coders are doing (such as messy versioning), then it's certainly worthwhile to create a design that prevents it. But the more I think about it, the more convinced I become that most of version()'s restrictions are just red herrings. I really think you're attacking the wrong thing (not that I have any idea where the ideal place to attack would be). After all, Denis and I have both demonstrated that D's version() is just as susceptible to mess as C's #if/#ifdef versioning. If people are going to make a version() mess, they're going to do it. Things like !, ||, && and expression-level versions are just drops in the pond, they would allow certain things to be cleaned up, but they're not going to break the dam any more than it already is, and they would even make a few things better. It would be great to have a way to eliminate messy versioning, but things such as prohibiting !, ||, && and expression-versions are doing very little to accomplish that. Modeling the version() syntax after the conditional syntax, and giving it less-than-"BEGIN"->"{"-level of power is already accomplishing far more in that regard. The benefits of preventing typos in version identifiers would also dwarf any benefits that might be gained from prohibiting !, ||, && and expression-versions.
Feb 10 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
I see your points, although I don't quite agree. Programmers will 
continue to do stupid things, even if it hurts. And in these cases, 
having an "intentionally hard to use" version-statement might increase 
the overall mess even further.

But what do you say about the possibility of uncatched typos? (the 
"version(linxu) {" example)
Feb 10 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
grauzone wrote:
 I see your points, although I don't quite agree. Programmers will
 continue to do stupid things, even if it hurts. And in these cases,
 having an "intentionally hard to use" version-statement might increase
 the overall mess even further.
 
 But what do you say about the possibility of uncatched typos? (the
 "version(linxu) {" example)

Versions are not boolean flags that are either true or false [1]. They're zero-state flags that either exist or don't. You can't spell-check something which simply may not exist. So let's say you have to define all version identifiers. The problem with this is that the more you use versioning, the more hideous your compile-lines are going to become... dmd foo --version=UseMMX=false --version=UseSSE=false --version=UseSSE2=true --version=Use3DNow=false --version=UseSSE2_AMD=false --version=UseSSE3=false --version=UseSSE4=false --version=UseSPARCExtension=false --version=UseAltivec=false --version=UseARMStuff=false --version=OhGodMakeItStop=false As opposed to dmd foo --version=UseSSE2 You might be able to rework the syntax so that you can define the default value of a version symbol in a module, then override it from the command line. module simd_versions; version UseMMX=false, UseSSE=false, UseSSE2=false, ...; -- Daniel [1] I'm ignoring integer versions which have a similar problem, but would just make this post longer :P
Feb 10 2009
parent "Nick Sabalausky" <a a.a> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
news:gmtf5g$1ban$1 digitalmars.com...
 --version=OhGodMakeItStop=false

lol :) I love all of the contrived code we come up with on this newsgroup. Other gems just from this "Why version()?" thread alone: version=canBeUsedToPickupChicks version(Poopy) {} else
 You might be able to rework the syntax so that you can define the
 default value of a version symbol in a module, then override it from the
 command line.

 module simd_versions;
 version UseMMX=false, UseSSE=false, UseSSE2=false, ...;

I think you've just demonstrated here that this feature *can* be made to work.
Feb 10 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Wed, 11 Feb 2009 01:33:12 +0300, Walter Bright <newshound1 digitalmars.com>
wrote:

 Steven Schveighoffer wrote:
 "Walter Bright" wrote
 2. version (A || B) can be done as:

     version (A) version=AorB;
     version (B) version=AorB;


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 */ ===============================

Ok, I've imagined that Boehm GC is written in D and went into a trouble of rewriting the #if/else/endif stuf into versioning blocks: version(NEED_FIND_LIMIT) version = NEED_FIND_LIMIT_or_UNIT_LIKE; version(UNIX_LIKE) version = NEED_FIND_LIMIT_or_UNIT_LIKE; version(NEED_FIND_LIMIT_or_UNIT_LIKE) { version (__STDC__) { typedef void (*handler)(int); } else { typedef void (*handler)(); } version (SUNOS5SIGS) version = SUNOS5SIGS_or_IRIX5_or_OSF1_or_HURD_or_NETBSD; version (IRIX5) version = SUNOS5SIGS_or_IRIX5_or_OSF1_or_HURD_or_NETBSD; version (OSF1) version = SUNOS5SIGS_or_IRIX5_or_OSF1_or_HURD_or_NETBSD; version (HURD) version = SUNOS5SIGS_or_IRIX5_or_OSF1_or_HURD_or_NETBSD; version (NETBSD) version = SUNOS5SIGS_or_IRIX5_or_OSF1_or_HURD_or_NETBSD; version (IRIX5) version = IRIX5_or_HPUX_or_HURD_or_NETBSD; version (HPUX) version = IRIX5_or_HPUX_or_HURD_or_NETBSD; version (HURD) version = IRIX5_or_HPUX_or_HURD_or_NETBSD; version (NETBSD) version = IRIX5_or_HPUX_or_HURD_or_NETBSD; version (SUNOS5SIGS_or_IRIX5_or_OSF1_or_HURD_or_NETBSD) { static struct sigaction old_segv_act; version (IRIX5_or_HPUX_or_HURD_or_NETBSD) { static struct sigaction old_bus_act; } } else { static handler old_segv_handler, old_bus_handler; } version (__STDC__) { void GC_set_and_save_fault_handler(handler h) } else { void GC_set_and_save_fault_handler(h) handler h; } { version(SUNOS5SIGS_or_IRIX5_or_OSF1_or_HURD_or_NETBSD) { struct sigaction act; act.sa_handler = h; version(false) { /* 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 (IRIX5_or_HPUX_or_HURD_or_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); } } } else { old_segv_handler = signal(SIGSEGV, h); version (SIGBUS) { old_bus_handler = signal(SIGBUS, h); } } } } Does it look any better? No way! Besides, C/C++/C# style directives so much more powerful that I'd happily exchange version them just to avoid code duplication hell in my source code files.
 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.

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

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.

Feb 10 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
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.

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.

"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 { ... }
Feb 10 2009
prev sibling next sibling parent "Tim M" <a b.com> writes:
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.

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.

"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.
Feb 10 2009
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
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/
Feb 10 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jason House wrote:
 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/

That is an interesting idea. I never thought of that.
Feb 10 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Walter Bright wrote:
 Jason House wrote:
 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/

That is an interesting idea. I never thought of that.

What about this? /+{{VersionIdentifier}} ... +/ The lexer doesn't have to know whether a particular version identifier is defined or not. That said, I don't know whether you could keep a "sort-of comment" around in the AST until the semantic pass; haven't had much luck deciphering the compiler yet. :P Plus, it's backwards-compatible! :D As for what's allowed in the {{..}}, you could always restrict it to a set of version identifiers that represent syntax-incompatible changes. -- Daniel
Feb 10 2009
next sibling parent Don <nospam nospam.com> writes:
Brian wrote:
 On Wed, 11 Feb 2009 08:51:10 +0000, Brian wrote:
 
 On Wed, 11 Feb 2009 14:07:29 +1100, Daniel Keep wrote:
 What about this?

 /+{{VersionIdentifier}} ... +/

is that just a neater way to do this? version(Identifier) { mixin(` //code! `)}

after writing that i realized the mixin is probably evaluated even if the version is false, that kind of destroys that idea.

mixin( version(Identifier) { `//code!` } else { ``});
Feb 11 2009
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
news:gmtfd9$1bjk$1 digitalmars.com...
 What about this?

 /+{{VersionIdentifier}} ... +/

One thing that's nice about that in a really bizarre sort of way is that none of the code editors I've tried support syntax highlighting of nested comments, so all the code inside there would automatically be properly highlighted ;-)
Feb 11 2009
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright Wrote:

 Jason House wrote:
 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/

That is an interesting idea. I never thought of that.

Would you be willing to add it as a new feature to both D1 and D2? I wonder how viable having one code base would be at that point? Would druntime be a good way to test that? Druntime would be a good guinea pig. I'd really hope a smooth transition for druntime would be enough to inspire more projects to support D1and D2 with a single code base.
Feb 11 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jason House wrote:
 Walter Bright Wrote:
 
 Jason House wrote:
 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/


Would you be willing to add it as a new feature to both D1 and D2? I wonder how viable having one code base would be at that point? Would druntime be a good way to test that? Druntime would be a good guinea pig. I'd really hope a smooth transition for druntime would be enough to inspire more projects to support D1and D2 with a single code base.

I don't see druntime supporting D1, it's pretty in bed with D2 features.
Feb 11 2009
parent Sean Kelly <sean invisibleduck.org> writes:
== Quote from Walter Bright (newshound1 digitalmars.com)'s article
 Jason House wrote:
 Walter Bright Wrote:

 Jason House wrote:
 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/


Would you be willing to add it as a new feature to both D1 and D2? I wonder how viable having one code base would be at that point? Would druntime be a good way to test that? Druntime would be a good guinea pig. I'd really hope a smooth transition for druntime would be enough to inspire more projects to support D1and D2 with a single code base.


There's a druntime branch that is compatible with D1. I created it mostly in case the Tango folks wanted to build off it, but that never happened and I've gotten a bit lazy about keeping it up to date. As you say however, as time goes on, the D1 branch will become increasingly difficult to keep in sync with the D2 version, since most new features will not be supported by the D1 compiler. I'm going to try and keep the branch operational for anyone who cares, but don't plan to add any new features to it since most of the new stuff requires D2 anyway. Sean
Feb 11 2009
prev sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Jason House escribió:
 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/

Why not have special versions like D1, D2, D3 for the compiler to treat them differently? If a version(DX) is found that is not the current's compiler version, it just consumes tokens (counting opening and closing of brackets, etc.) until the curly's close. Much like the asm statement.
Feb 10 2009
parent Ary Borenszweig <ary esperanto.org.ar> writes:
Ary Borenszweig escribió:
 Jason House escribió:
 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/

Why not have special versions like D1, D2, D3 for the compiler to treat them differently? If a version(DX) is found that is not the current's compiler version, it just consumes tokens (counting opening and closing of brackets, etc.) until the curly's close. Much like the asm statement.

Ah, mmm... the lexer also needs to know this. Doesn't work. :-( Well, it could work if you lookahead a few tokens...
Feb 10 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Tue, Feb 10, 2009 at 8:50 PM, Walter Bright
<newshound1 digitalmars.com> wrote:

 I'll argue that I've never seen anyone create such a mess in D, while I see
 it regularly in C. So something about D is discouraging developing those
 things.

D is also only _really truly_ supported on about three or four platforms, has been around for a fraction of the time C has, and has a core code-producing community of I'd say around 100~150 people. Sample bias.
 I think the tipping point is that it's too easy in C to slip into writing
 such a mess without actually trying to, while in D you have to work harder
 to do it. Hard enough that one might as well do it better in the first
 place.

If nothing - NOTHING - else, please give us version(!something). Out of everything wrong with it, that is the worst. You say that according to "cognitive studies" that people don't see the !; then why didn't you remove it from the rest of the language? And how is THIS: version(Poopy) {} else { } supposedly better?!
Feb 10 2009
prev sibling next sibling parent Steve Schveighoffer <schveiguy yahoo.com> writes:
On Tue, 10 Feb 2009 19:28:46 -0800, Walter Bright wrote:

 Steven Schveighoffer wrote:
 That example has nothing to do with || being allowed, and nothing to do
 with your exhibit B.  Your simple example makes sense to split into
 multiple files, but you simply waved your magic "split into multiple
 files" wand at Hans' code and because of the simplicity of D's
 versioning system, it's more maintainable?  I find it hard to believe
 without proof.

I cannot prove to you in any mathematical sort of way. All I can say is that I have a lot of experience doing things both ways, and what the results are. I can readily believe that it isn't obvious until one has done it both ways; programming is full of examples of such. Such as: #define BEGIN { #define END } I can't prove that's a bad idea, either. But experience shows it is <g>.

It's bad. I agree with you. I also have bad experiences with the preprocessor, one of the reasons I looked for something like D in the first place, and one of the reasons I'm leary of macros. I'm not asking for the C preprocessor. I'm asking for a bit of syntax sugar to avoid unmaintainable tables of expanded if statements. What I mean by expanded if statements is that you are making me expand: if(a || b) statement; into bool aorb = false; if(a) aorb = true; if(b) aorb = true; if(c) statement; Let's take a trip to analogy land. It's like you are saying you have been bit by a tiger in the past. Therefore, you dislike all animals, because they remind you of tigers, and who could blame you. But because of your position of power, you have passed a law that *nobody* can like animals, for their own good. Why not just avoid animals on your own, and let others have their tame animals?
 
 
 Besides, your example exists *without* any new features to the
 versioning system.  That is, your example just shows how it's easier to
 maintain if you put the version identifier at the outermost layer,
 instead of in the innermost layer, and I agree with you there.  I just
 want to try and get rid of the cruft of:
 
 version(A)
    version = AorB;
 version(B)
    version = AorB;

I think I've been misunderstood here. I never intended one to actually use the "or" string in a version, my intent was that one determine just what the feature is that is AorB, and name the identifier that. For example: version (linux) version = Pthreads; version (OSX) version = Pthreads; version (Pthreads) ... else version (Windows) ... I don't consider that cruft, but very reasonable self-documentation.

This is still cruft. I'd consider this to be more succinct and just as self documenting (as well as more maintainable, I no longer have to change the string Pthreads in multiple locations in the declaration of it). version(linux || OSX) version = Pthreads; ...
 
 
 If you want to prove how much easier it is, actually split Hans' rat's
 nest into multiple files how you would do it, and I'll show you that it
 isn't any harder to maintain if you use ||.

I already did port it once, many years ago, it's why I know how hard it is. Take a look at D's gc which uses personality modules. I ported that to the mac pretty easily and never touched the main code. I can't even figure out where the mac osx code is in the hbgc or what would need to be changed for it. I couldn't even figure out if the hbgc supported OSX or not. To sum up, I've done it both ways. The personality module method approach is head and shoulders the clear winner.

That's all fine, and I pretty much agree with you, but I think we are arguing apples and oranges. In what way does using || in a version statement prevent or even discourage you from separating out your versions into modules based on platforms or personality? It seems like a design preference that is orthogonal to this feature. For instance, two developers quite easily translated the complicated C preprocessor example you gave into version statements for the *current* version system. So it's still possible to have unmaintainable, unreadable code without || as a feature. All I'm asking is to make this: version(x || y) turn into this: version(x) version = xory; version(y) version = xory; version(xory) automatically by the compiler. I shouldn't have to do that bit of manual compilation. -Steve
Feb 10 2009
prev sibling next sibling parent Steve Schveighoffer <schveiguy yahoo.com> writes:
On Wed, 11 Feb 2009 05:43:48 +0000, Steve Schveighoffer wrote:

 What I mean by
 expanded if statements is that you are making me expand:
 
 if(a || b)
   statement;
 
 into
 
 bool aorb = false;
 if(a)
   aorb = true;
 if(b)
   aorb = true;
 if(c)
   statement;

This should have been if(aorb) statement; -Steve
Feb 10 2009
prev sibling next sibling parent Brian <digitalmars brianguertin.com> writes:
On Wed, 11 Feb 2009 14:07:29 +1100, Daniel Keep wrote:
 What about this?
 
 /+{{VersionIdentifier}} ... +/

is that just a neater way to do this? version(Identifier) { mixin(` //code! `)}
Feb 11 2009
prev sibling next sibling parent Brian <digitalmars brianguertin.com> writes:
On Wed, 11 Feb 2009 08:51:10 +0000, Brian wrote:

 On Wed, 11 Feb 2009 14:07:29 +1100, Daniel Keep wrote:
 What about this?
 
 /+{{VersionIdentifier}} ... +/

is that just a neater way to do this? version(Identifier) { mixin(` //code! `)}

after writing that i realized the mixin is probably evaluated even if the version is false, that kind of destroys that idea.
Feb 11 2009
prev sibling next sibling parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter Bright wrote:

 1. Why not version(!feature) ?
 
 Because cognitive studies show that people just don't see the negation. 
 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.

And for code that can't/won't be rewritten, there's the workaround: version (linux) {} else { ... } But yeah, using Unix or Posix would be better than using !Windows. (only problem there being that both those versions are in use now) --anders
Feb 11 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Wed, Feb 11, 2009 at 9:49 AM, Joel C. Salomon <joelcsalomon gmail.com> w=
rote:
 version(!Windows)
    // some added functionality that depends on UNIX-like semantics

 Then port the code to something oddball=97Plan 9, say, or VMS.

I use versions for things other than platform-specific code. Like library compilation options.
Feb 11 2009
prev sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Wed, Feb 11, 2009 at 2:14 PM, Joel C. Salomon <joelcsalomon gmail.com> wrote:
 Jarrett Billingsley wrote:
 I use versions for things other than platform-specific code.  Like
 library compilation options.

So what would your use-case look like?

version(TurnOffSomeFeature) {} else { codeForFeature(); } Sometimes you want to have things that turn _off_ code from a default of being on. I know, I know, Walter said that "[v]ersions should be positive things, because a version is something that is being build - one doesn't craft a makefile to build a notLinux." But the fact is, platform-specific stuff and code options are two very different use cases. Platform-specific things are completely invisible to the user of a library and only one configuration can ever be in use on a single platform. Code options, on the other hand, are based on user preference. Some things are extra, and it makes sense to have switches to turn them on. But some things are the other way, where you want them *on* by default and want to have the option of turning them *off*.
Feb 11 2009