www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Improving version(...)

reply F. Almeida <francisco.m.almeida gmail.com> writes:
The version() { ... } blocks would greatly improve from support of
boolean operators, which would make code much more readable.

Let us assume, for example, that I have several version identifiers
(VERSION1, VERSION2, VERSION3, VERSION4, etc.) and that a block of
code may be compiled in the cases of VERSION1 or VERSION3. It would
prove the simplest if one could simply use boolean operators to
express combinations of valid versions and thus be able to do


version(VERSION1 || VERSION3)
{
 // ...
}
Oct 18 2010
next sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Mon, 18 Oct 2010 19:15:47 +0300, F. Almeida  
<francisco.m.almeida gmail.com> wrote:

 The version() { ... } blocks would greatly improve from support of
 boolean operators, which would make code much more readable.

 Let us assume, for example, that I have several version identifiers
 (VERSION1, VERSION2, VERSION3, VERSION4, etc.) and that a block of
 code may be compiled in the cases of VERSION1 or VERSION3. It would
 prove the simplest if one could simply use boolean operators to
 express combinations of valid versions and thus be able to do


 version(VERSION1 || VERSION3)
 {
  // ...
 }

I remember reading somewhere that this limitation is deliberate. I believe the reason for this is that programmers with a C background will tend to abuse this feature to create near-unreadable code (#ifdef mess). The solution (workaround) for this, is to create new versions for specific features you wish to enable/disable. For example: version(Demo) {} else version(Lite) {} else { version = EnableFeatureX; } version(EnableFeatureX) { ... } One thing people seem to agree with is that version statements could use the negation (!) operator. Then the above could be written as: version(!Demo) version(!Lite) { version = EnableFeatureX; } -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Oct 18 2010
parent reply Don <nospam nospam.com> writes:
Vladimir Panteleev wrote:
 On Mon, 18 Oct 2010 19:15:47 +0300, F. Almeida 
 <francisco.m.almeida gmail.com> wrote:
 
 The version() { ... } blocks would greatly improve from support of
 boolean operators, which would make code much more readable.

 Let us assume, for example, that I have several version identifiers
 (VERSION1, VERSION2, VERSION3, VERSION4, etc.) and that a block of
 code may be compiled in the cases of VERSION1 or VERSION3. It would
 prove the simplest if one could simply use boolean operators to
 express combinations of valid versions and thus be able to do


 version(VERSION1 || VERSION3)
 {
  // ...
 }

I remember reading somewhere that this limitation is deliberate. I believe the reason for this is that programmers with a C background will tend to abuse this feature to create near-unreadable code (#ifdef mess).

Yes. Unfortunately the current situation doesn't really prevent that. My opinion (a significant departure from the current situation, unfortunately): It should be possible to define version identifiers with booleans. version EnableFeatureX = !Lite && !Demo; Possibly: * make it illegal to define a version identifier from inside a version {} block. * disallow duplicate version definitions. * require declarations for all version identifiers. Versions which are set from the command line should be explicitly declared, eg: version Lite = extern; version Demo = extern; That would make creating a bird's nest impossible. And currently, you can make a typo like: version(Linix) {} and it compiles happily. I don't like that. Especially when we have builtin names like D_Inline_Asm_X86_64!
Oct 18 2010
next sibling parent klickverbot <see klickverbot.at> writes:
On 10/18/10 9:56 PM, Simen kjaeraas wrote:
 * require declarations for all version identifiers. Versions which are
 set from the command line should be explicitly declared, eg:
 version Lite = extern;
 version Demo = extern;

 That would make creating a bird's nest impossible.
 And currently, you can make a typo like:
 version(Linix) {}
 and it compiles happily. I don't like that. Especially when we have
 builtin names like D_Inline_Asm_X86_64!

This is an awesome idea. ++votes

+1 from me too, this could be one of the key parts of a long outstanding version() overhaul. What also bugs me about the current situation (despite the fact that I think numeric versions should be removed, but that's another story) is that the equals sign to define a new version seems very illogical – »version ~= someUserDefinedVersion« would make much more sense to me…
Oct 18 2010
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 On Monday, October 18, 2010 12:24:48 Don wrote:
 And currently, you can make a typo like:
 version(Linix) {}
 and it compiles happily. I don't like that. Especially when we have
 builtin names like D_Inline_Asm_X86_64!

The real question is how to cleanly fix that. A version is defined only if it's in use, so it can't do like variable declarations do and complain that the version wasn't declared. You could even be stupid enough to declare your own version D_Inline_Asm_X86_65, so it's not even like the compiler can necessarily complain when a version declaration uses a version which is almost the same as a correct one but not quite (since you could just not be compiling with that version declared at the moment).

If built-in versions are inside some kind of enum or namespace, the compiler can tell apart the built-in ones from all the other ones, so if you write: version(std.D_Inline_Asm_X86_65) The compiler will complain that doesn't exists among the standard ones. If you want to use your own ones you just don't use the "std." prefix: version(myFoo) Bye, bearophile
Oct 19 2010
parent reply Jesse Phillips <jessekphillips+D gmail.com> writes:
bearophile Wrote:

 If built-in versions are inside some kind of enum or namespace, the compiler
can tell apart the built-in ones from all the other ones, so if you write:
 version(std.D_Inline_Asm_X86_65)
 The compiler will complain that doesn't exists among the standard ones.
 
 If you want to use your own ones you just don't use the "std." prefix:
 version(myFoo)
 
 Bye,
 bearophile

Note that the latest highlighting file for Vim will highlight the known built-in version Identifiers. I don't have much of a suggestion for solving the real issue, but maybe the compiler could return a list of versions used? Though that brings to question what happens to versions used in side versions.
Oct 19 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Jesse Phillips" <jessekphillips+D gmail.com> wrote in message 
news:i9l935$5ul$1 digitalmars.com...
 bearophile Wrote:

 If built-in versions are inside some kind of enum or namespace, the 
 compiler can tell apart the built-in ones from all the other ones, so if 
 you write:
 version(std.D_Inline_Asm_X86_65)
 The compiler will complain that doesn't exists among the standard ones.

 If you want to use your own ones you just don't use the "std." prefix:
 version(myFoo)

 Bye,
 bearophile

Note that the latest highlighting file for Vim will highlight the known built-in version Identifiers. I don't have much of a suggestion for solving the real issue, but maybe the compiler could return a list of versions used? Though that brings to question what happens to versions used in side versions.

Clearly, the current version system is a suboptimal design. Unfortunately, given the specific nature of it, it doesn't appear to be possible work around this particular issue. And I don't think it's possible to sidestep it with a library-based alternative either, due to D's apperent inability to have deterministic cross-module global mutable state at compile time (see the thread "[challenge] Limitation in D's metaprogramming"). So I think aside from a pre-processor, the only real way to solve this issue is to redesign the version system to not work on a basis of "defined/not defined". I would certainly be in favor of that, but I suspect it would probably mean a D3 (or at least a D2.5 - " version" anyone?). Although, here's another idea that might at least help (athough not nearly as cleanly as a redesigned version system): There could be a "__traits(allVersionIdents)" that returns an array of all identifiers that are actually used anywhere in the program, *even* ones that turn out to be undefined. Or maybe a __traits that returns *just* the version identifiers that are used but undefined. This list is logically (if not physically) built up *before* any "version{}", "debug{}", or "static if" conditionals are actually evaluated (but after any string mixins, so that might be tricky, if even possible). Then, you can validate that list against all of the identifiers that you expect to possibly be used. There would also be a "__traits(builtinVersionIdents)" to aid with this.
Oct 19 2010
parent "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:i9ldub$f7h$1 digitalmars.com...
 "Jesse Phillips" <jessekphillips+D gmail.com> wrote in message 
 news:i9l935$5ul$1 digitalmars.com...
 bearophile Wrote:

 If built-in versions are inside some kind of enum or namespace, the 
 compiler can tell apart the built-in ones from all the other ones, so if 
 you write:
 version(std.D_Inline_Asm_X86_65)
 The compiler will complain that doesn't exists among the standard ones.

 If you want to use your own ones you just don't use the "std." prefix:
 version(myFoo)

 Bye,
 bearophile

Note that the latest highlighting file for Vim will highlight the known built-in version Identifiers. I don't have much of a suggestion for solving the real issue, but maybe the compiler could return a list of versions used? Though that brings to question what happens to versions used in side versions.

Clearly, the current version system is a suboptimal design. Unfortunately, given the specific nature of it, it doesn't appear to be possible work around this particular issue. And I don't think it's possible to sidestep it with a library-based alternative either, due to D's apperent inability to have deterministic cross-module global mutable state at compile time (see the thread "[challenge] Limitation in D's metaprogramming"). So I think aside from a pre-processor, the only real way to solve this issue is to redesign the version system to not work on a basis of "defined/not defined". I would certainly be in favor of that, but I suspect it would probably mean a D3 (or at least a D2.5 - " version" anyone?). Although, here's another idea that might at least help (athough not nearly as cleanly as a redesigned version system): There could be a "__traits(allVersionIdents)" that returns an array of all identifiers that are actually used anywhere in the program, *even* ones that turn out to be undefined. Or maybe a __traits that returns *just* the version identifiers that are used but undefined. This list is logically (if not physically) built up *before* any "version{}", "debug{}", or "static if" conditionals are actually evaluated (but after any string mixins, so that might be tricky, if even possible). Then, you can validate that list against all of the identifiers that you expect to possibly be used. There would also be a "__traits(builtinVersionIdents)" to aid with this.

Now that I think of it, it might not even be necessary for the list of used-but-undefined version identifiers to be built up before "version"/"static if"-etc is evaluated: version(Windows) { version(Oops_This_One_Is_A_Typo) } void main() { string[] allPossibleVersions = [/+...+/]; assert(/+ __traits(allVersionIdents) is a subset of allPossibleVersions +/); } Compiling for Linux will fail to catch the version typo. However, clearly the code is intended to occasionally be compiled for windows, and when it does you'll be able to detect that "Oops_This_One_Is_A_Typo" is misspelled. But what if "version(Windows)" is misspelled as "Widnoes" which causes "Oops_This_One_Is_A_Typo" to *never* be in the __traits(allVersionIdents) list? No problem: You'll catch the "Widnoes" error anyway no matter how it's compiled, and once you fix that, then you'll get the error with "Oops_This_One_Is_A_Typo". So I think this could work. The only tricky thing I see with whole approach is: what to do about libraries that have their own set of possible version identifiers? That would require some extra thought, but I don't think it's insurmountable.
Oct 19 2010
prev sibling parent Don <nospam nospam.com> writes:
Jonathan M Davis wrote:
 On Monday, October 18, 2010 12:24:48 Don wrote:
 And currently, you can make a typo like:
 version(Linix) {}
 and it compiles happily. I don't like that. Especially when we have
 builtin names like D_Inline_Asm_X86_64!

The real question is how to cleanly fix that.

Oct 19 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 18 Oct 2010 12:15:47 -0400, F. Almeida  
<francisco.m.almeida gmail.com> wrote:

 The version() { ... } blocks would greatly improve from support of
 boolean operators, which would make code much more readable.

 Let us assume, for example, that I have several version identifiers
 (VERSION1, VERSION2, VERSION3, VERSION4, etc.) and that a block of
 code may be compiled in the cases of VERSION1 or VERSION3. It would
 prove the simplest if one could simply use boolean operators to
 express combinations of valid versions and thus be able to do


 version(VERSION1 || VERSION3)
 {
  // ...
 }

This has been discussed heavily in the past. Walter doesn't want to do it, and this is one of those "Walter knows best" deals, don't bother trying. -Steve
Oct 18 2010
prev sibling next sibling parent reply =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= <just ask.me> writes:
Dnia 18-10-2010 o 18:15:47 F. Almeida <francisco.m.almeida gmail.com>  =

napisa=B3(a):

 The version() { ... } blocks would greatly improve from support of
 boolean operators, which would make code much more readable.

 Let us assume, for example, that I have several version identifiers
 (VERSION1, VERSION2, VERSION3, VERSION4, etc.) and that a block of
 code may be compiled in the cases of VERSION1 or VERSION3. It would
 prove the simplest if one could simply use boolean operators to
 express combinations of valid versions and thus be able to do


 version(VERSION1 || VERSION3)
 {
  // ...
 }

You can have it now: template isVersion(string ver) { enum bool isVersion =3D !is(typeof({ mixin("version(" ~ ver ~") static assert(0);"); })); } static if (isVersion"VERSION1" || isVersion!"VERSION3") { ... } If you're rushing to reply "That's hideous!", don't bother. I know. -- Tomek
Oct 18 2010
next sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Mon, 18 Oct 2010 21:42:20 +0200, Tomek Sowiński wrote:

 Dnia 18-10-2010 o 18:15:47 F. Almeida <francisco.m.almeida gmail.com>
 napisał(a):
 
 The version() { ... } blocks would greatly improve from support of
 boolean operators, which would make code much more readable.

 Let us assume, for example, that I have several version identifiers
 (VERSION1, VERSION2, VERSION3, VERSION4, etc.) and that a block of code
 may be compiled in the cases of VERSION1 or VERSION3. It would prove
 the simplest if one could simply use boolean operators to express
 combinations of valid versions and thus be able to do


 version(VERSION1 || VERSION3)
 {
  // ...
 }

You can have it now: template isVersion(string ver) { enum bool isVersion = !is(typeof({ mixin("version(" ~ ver ~") static assert(0);"); })); } static if (isVersion"VERSION1" || isVersion!"VERSION3") { ... } If you're rushing to reply "That's hideous!", don't bother. I know.

Actually, I think it's pretty cool. :) -Lars
Oct 18 2010
prev sibling next sibling parent =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= <just ask.me> writes:
Dnia 18-10-2010 o 22:39:32 Lars T. Kyllingstad  =

<public kyllingen.nospamnet> napisa=B3(a):

 version(VERSION1 || VERSION3)
 {
  // ...
 }

You can have it now: template isVersion(string ver) { enum bool isVersion =3D !is(typeof({ mixin("version(" ~ ver ~") static assert(0);"); })); } static if (isVersion"VERSION1" || isVersion!"VERSION3") { ... } If you're rushing to reply "That's hideous!", don't bother. I know.


Heh, thanks. Maybe with more templates it won't poke the eye: static if (anySatisfy!(isVersion, "VERSION1", "VERSION2", "VERSION3", ..= .)) Still, it is a hack around a language's restriction. But what would be = programming without one! :) -- = Tomek
Oct 18 2010
prev sibling parent Rainer Deyke <rainerd eldwood.com> writes:
On 10/18/2010 13:42, Tomek Sowiski wrote:
 template isVersion(string ver) {
     enum bool isVersion = !is(typeof({
           mixin("version(" ~ ver ~") static assert(0);");
     }));
 }
 
 static if (isVersion"VERSION1" || isVersion!"VERSION3") {
     ...
 }
 
 If you're rushing to reply "That's hideous!", don't bother. I know.

Hideous? That's beautiful! It lets me pretend that the ugly, restrictive, pointless "version" construct in the language didn't exist, while still reaping the benefits of the version construct. If I ever write a style guide for D, it will forbid the direct use of 'version' and require that 'isVersion' be used instead. -- Rainer Deyke - rainerd eldwood.com
Oct 18 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Don <nospam nospam.com> wrote:

 My opinion (a significant departure from the current situation,  
 unfortunately):
 It should be possible to define version identifiers with booleans.

 version EnableFeatureX = !Lite && !Demo;

This looks nice to me, except that the rhs looks like normal code. It gives me the feeling that version foo = sqrt(bar) should work. Perhaps version foo = version( bar && !baz ); is better. I'm not sure.
 Possibly:
 * make it illegal to define a version identifier from inside a version  
 {} block.

This would break existing code, and it feels logical to write code like this: version( foo ) { // quite a few lines of dependent code version = bar; } Of course, the result is cleaner: version( foo ) { // quite a few lines of dependent code } version bar = foo; So I'm a bit torn. Wait, getting a brain wave again.... Ahh. Make that a ++votes.
 * disallow duplicate version definitions.

Absolutely.
 * require declarations for all version identifiers. Versions which are  
 set from the command line should be explicitly declared, eg:
 version Lite = extern;
 version Demo = extern;

 That would make creating a bird's nest impossible.
 And currently, you can make a typo like:
 version(Linix) {}
 and it compiles happily. I don't like that. Especially when we have  
 builtin names like D_Inline_Asm_X86_64!

This is an awesome idea. ++votes -- Simen
Oct 18 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I've never understood this syntax, it doesn't play well with the rest
of the language at all. ~=3D makes a lot more sense.

On 10/19/10, klickverbot <see klickverbot.at> wrote:
 On 10/18/10 9:56 PM, Simen kjaeraas wrote:
 What also bugs me about the current situation (despite the fact that I
 think numeric versions should be removed, but that's another story) is
 that the equals sign to define a new version seems very illogical =96
 =BBversion ~=3D someUserDefinedVersion=AB would make much more sense to m=


Oct 18 2010
prev sibling next sibling parent =?utf-8?B?VG9tZWsgU293acWEc2tp?= <just ask.me> writes:
Dnia 19-10-2010 o 00:20:29 klickverbot <see klickverbot.at> napisa=C5=82=
(a):

 On 10/18/10 9:56 PM, Simen kjaeraas wrote:
 * require declarations for all version identifiers. Versions which a=



 set from the command line should be explicitly declared, eg:
 version Lite =3D extern;
 version Demo =3D extern;

 That would make creating a bird's nest impossible.
 And currently, you can make a typo like:
 version(Linix) {}
 and it compiles happily. I don't like that. Especially when we have
 builtin names like D_Inline_Asm_X86_64!

This is an awesome idea. ++votes

+1 from me too, this could be one of the key parts of a long outstandi=

 version() overhaul.

 What also bugs me about the current situation (despite the fact that I=

 think numeric versions should be removed, but that's another story) is=

 that the equals sign to define a new version seems very illogical =E2=80=

 =C2=BBversion ~=3D someUserDefinedVersion=C2=AB would make much more s=

I like the explicit version declarations too. My share of bikeshedding: /** Possible versions of AwesomestApp. */ version AweHome, AwePro, AweSome; version AweExp; /// Experimental AwesomestApp. -- = Tomek
Oct 19 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, October 18, 2010 12:24:48 Don wrote:
 And currently, you can make a typo like:
 version(Linix) {}
 and it compiles happily. I don't like that. Especially when we have
 builtin names like D_Inline_Asm_X86_64!

The real question is how to cleanly fix that. A version is defined only if it's in use, so it can't do like variable declarations do and complain that the version wasn't declared. You could even be stupid enough to declare your own version D_Inline_Asm_X86_65, so it's not even like the compiler can necessarily complain when a version declaration uses a version which is almost the same as a correct one but not quite (since you could just not be compiling with that version declared at the moment). So, while I agree that the current situation with versions and mispelling them is problematic, I haven't a clue how you'd fix it right now. If _all_ versions had to be declared and then only the enabled ones were compiled in, _then_ we could make it work, since the entire list of versions would be known, but that would be a definite departure from how things work now and would likely make it very hard to do version = otherVersion; in your code like you can do now. - Jonathan M Davis
Oct 19 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, October 19, 2010 15:50:12 bearophile wrote:
 Jonathan M Davis:
 On Monday, October 18, 2010 12:24:48 Don wrote:
 And currently, you can make a typo like:
 version(Linix) {}
 and it compiles happily. I don't like that. Especially when we have
 builtin names like D_Inline_Asm_X86_64!

The real question is how to cleanly fix that. A version is defined only if it's in use, so it can't do like variable declarations do and complain that the version wasn't declared. You could even be stupid enough to declare your own version D_Inline_Asm_X86_65, so it's not even like the compiler can necessarily complain when a version declaration uses a version which is almost the same as a correct one but not quite (since you could just not be compiling with that version declared at the moment).

If built-in versions are inside some kind of enum or namespace, the compiler can tell apart the built-in ones from all the other ones, so if you write: version(std.D_Inline_Asm_X86_65) The compiler will complain that doesn't exists among the standard ones. If you want to use your own ones you just don't use the "std." prefix: version(myFoo) Bye, bearophile

That would certainly help with the standard ones, and it could be a very good move to make, but it doesn't solve the problem in the general case. - Jonathan M Davis
Oct 19 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, October 19, 2010 16:50:31 Don wrote:
 Jonathan M Davis wrote:
 On Monday, October 18, 2010 12:24:48 Don wrote:
 And currently, you can make a typo like:
 version(Linix) {}
 and it compiles happily. I don't like that. Especially when we have
 builtin names like D_Inline_Asm_X86_64!

The real question is how to cleanly fix that.

Did you read my post? I posted a solution to that!

Ouch. So you did. I'd read it a while ago and was thinking about it and responded without fully reading it again. Foolish of me. Sorry about that. - Jonathan M Davis
Oct 19 2010