www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Version block "conditions" with logical operators

reply Tomer Filiba <tomerfiliba gmail.com> writes:
Hey guys,

Looking at our code base (weka), I realized it would be really 
useful if we had logical operators (negation, conjunction, 
disjunction) in the version's "condition" clause. Here's what I 
have in mind:

version(!extra_checks) {
     ...
}

version(dlang_ver_2069 || dlang_ver_2070) {
     ...
}

Today we have lots of ugly code like

version(unittest) {} else { ... }

and bad-old copy-paste for the logical-or case I mentioned.

Is there any reason for that versions can't take compound logical 
conditions? Alternatively, an isVersion(x) predicate that I could 
use in a static if could do the trick -- although I think 
``version(x || y)`` is more readable than ``static if 
(isVersion(x) || isVersion(y))``

I think it could be a useful feature. Any thoughts?

-tomer
May 10 2016
next sibling parent reply Joakim <dlang joakim.fea.st> writes:
On Tuesday, 10 May 2016 at 11:12:58 UTC, Tomer Filiba wrote:
 Hey guys,

 Looking at our code base (weka), I realized it would be really 
 useful if we had logical operators (negation, conjunction, 
 disjunction) in the version's "condition" clause. Here's what I 
 have in mind:

 version(!extra_checks) {
     ...
 }

 version(dlang_ver_2069 || dlang_ver_2070) {
     ...
 }

 Today we have lots of ugly code like

 version(unittest) {} else { ... }

 and bad-old copy-paste for the logical-or case I mentioned.

 Is there any reason for that versions can't take compound 
 logical conditions? Alternatively, an isVersion(x) predicate 
 that I could use in a static if could do the trick -- although 
 I think ``version(x || y)`` is more readable than ``static if 
 (isVersion(x) || isVersion(y))``

 I think it could be a useful feature. Any thoughts?

 -tomer
This has been discussed multiple times in the forum, Walter is against it and I agree with him: http://forum.dlang.org/thread/op.xz6shob04sdys0 nicolass-macbook-pro.local http://forum.dlang.org/thread/n0u5v3$1lsh$1 digitalmars.com Static if can do this now using enums that you define by using those version conditions, but Walter advises against it. Another alternative is to put all such OS versioning logic in a build script somewhere and then version on features in your D code, which is probably cleanest, ie have a build script check if you're building for an OS that supports LIBRARY_X, then pass -version=LIBRARY_X to your build and version your D code using version(LIBRARY_X). If you use reggae (https://github.com/atilaneves/reggae), the logic that checks if you're using LIBRARY_X could be written in D too. The idea is to avoid having a bunch of such repeated conditional logic for versioning spread throughout the codebase, because it will be very error-prone and brittle.
May 10 2016
parent Tomer Filiba <tomerfiliba gmail.com> writes:
On Tuesday, 10 May 2016 at 11:48:12 UTC, Joakim wrote:
 This has been discussed multiple times in the forum, Walter is 
 against it and I agree with him:
Thanks for the pointers
 Another alternative is to put all such OS versioning logic in a 
 build script somewhere and then version on features in your D 
 code, which is probably cleanest, ie have a build script check
...
 The idea is to avoid having a bunch of such repeated 
 conditional logic for versioning spread throughout the 
 codebase, because it will be very error-prone and brittle.
It's compiler-version-dependent, or version(unittest) or version(assert) or version(do_extra_checks). Which means my checks are inherently spread around the code. I counted 53 instances of "version.*else" in our code, which all look like version (XXX) {} else {...} I'm going to use my isVersion hack to avoid code duplication in "or", but for the rest of the code, I really think a version(!unittest) {...} is cleaner. -tomer
May 10 2016
prev sibling next sibling parent reply Tomer Filiba <tomerfiliba gmail.com> writes:
On Tuesday, 10 May 2016 at 11:12:58 UTC, Tomer Filiba wrote:
 Alternatively, an isVersion(x) predicate that I could use in a 
 static if could do the trick
Well, I've come up with template isVersion(string ver) { mixin(format(q{ version(%s) { enum isVersion = true; } else { enum isVersion = false; } }, ver)); } pragma(msg, isVersion!"foo"); // false pragma(msg, isVersion!"assert"); // true But it feels hackish too -tomer
May 10 2016
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/10/16 2:48 PM, Tomer Filiba wrote:
 On Tuesday, 10 May 2016 at 11:12:58 UTC, Tomer Filiba wrote:
 Alternatively, an isVersion(x) predicate that I could use in a static
 if could do the trick
Well, I've come up with template isVersion(string ver) { mixin(format(q{ version(%s) { enum isVersion = true; } else { enum isVersion = false; } }, ver)); } pragma(msg, isVersion!"foo"); // false pragma(msg, isVersion!"assert"); // true But it feels hackish too
Not if we put it in the standard library :o). -- Andrei
May 10 2016
prev sibling parent Artur Skawina via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 05/10/16 13:48, Tomer Filiba via Digitalmars-d wrote:
 On Tuesday, 10 May 2016 at 11:12:58 UTC, Tomer Filiba wrote:
 Alternatively, an isVersion(x) predicate that I could use in a static if could
do the trick
Well, I've come up with template isVersion(string ver) { mixin(format(q{ version(%s) { enum isVersion = true; } else { enum isVersion = false; } }, ver)); } pragma(msg, isVersion!"foo"); // false pragma(msg, isVersion!"assert"); // true But it feels hackish too
It is a hack; the main problem being that the check happens at the `isVersion` definition, and not where it's used. But, if you only care about "global" identifiers, you can just use something like this: ----------------------------------------------------- module mver; struct ver { template opDispatch(string M) { mixin(` version(`~M~`) enum opDispatch = true; else enum opDispatch = false; `); } } ----------------------------------------------------- import mver; static if (ver.linux && !ver.D_LP64) { /* ... */ } ----------------------------------------------------- artur
May 11 2016
prev sibling next sibling parent reply Johan Engelen <j j.nl> writes:
On Tuesday, 10 May 2016 at 11:12:58 UTC, Tomer Filiba wrote:
 Hey guys,

 Looking at our code base (weka), I realized it would be really 
 useful if we had logical operators (negation, conjunction, 
 disjunction) in the version's "condition" clause.
We resort to enums whenever 'version' is not adequate like this: https://github.com/ldc-developers/ldc/blob/master/ddmd/globals.d#L18-L45
May 10 2016
parent reply Tomer Filiba <tomerfiliba gmail.com> writes:
On Tuesday, 10 May 2016 at 12:27:19 UTC, Johan Engelen wrote:
 We resort to enums whenever 'version' is not adequate like this:
 https://github.com/ldc-developers/ldc/blob/master/ddmd/globals.d#L18-L45
A good example -- which only proves how the current version() block is insufficient. So instead of version(xxx), which states your intent clearly, you get lots of non-idiomatic static ifs, which are more cumbersome and error prone.
May 10 2016
parent reply Joakim <dlang joakim.fea.st> writes:
On Tuesday, 10 May 2016 at 14:47:05 UTC, Tomer Filiba wrote:
 On Tuesday, 10 May 2016 at 12:27:19 UTC, Johan Engelen wrote:
 We resort to enums whenever 'version' is not adequate like 
 this:
 https://github.com/ldc-developers/ldc/blob/master/ddmd/globals.d#L18-L45
A good example -- which only proves how the current version() block is insufficient. So instead of version(xxx), which states your intent clearly, you get lots of non-idiomatic static ifs, which are more cumbersome and error prone.
That example is misleading, as that was translated from C++ and the host half of it was removed a couple months ago: https://github.com/dlang/dmd/pull/5549/files I'll submit a PR for the rest: I'm sick of this argument that "ddmd is using static if, so why shouldn't I?"
May 11 2016
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 5/11/2016 6:52 PM, Joakim wrote:
 That example is misleading, as that was translated from C++ and the host half
of
 it was removed a couple months ago:

 https://github.com/dlang/dmd/pull/5549/files

 I'll submit a PR for the rest: I'm sick of this argument that "ddmd is using
 static if, so why shouldn't I?"
Please do. That code is an abomination.
May 11 2016
parent reply Joakim <dlang joakim.fea.st> writes:
On Thursday, 12 May 2016 at 01:58:33 UTC, Walter Bright wrote:
 On 5/11/2016 6:52 PM, Joakim wrote:
 That example is misleading, as that was translated from C++ 
 and the host half of
 it was removed a couple months ago:

 https://github.com/dlang/dmd/pull/5549/files

 I'll submit a PR for the rest: I'm sick of this argument that 
 "ddmd is using
 static if, so why shouldn't I?"
Please do. That code is an abomination.
I'm trying, but Daniel seems against it, care to chip in? https://github.com/dlang/dmd/pull/5772 Specifically, do you want the changes in that PR? If so, do you prefer the use of TARGET_POSIX as a runtime variable or listing each TARGET_OS separately?
May 13 2016
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/13/2016 1:57 AM, Joakim wrote:
 I'm trying, but Daniel seems against it, care to chip in?

 https://github.com/dlang/dmd/pull/5772

 Specifically, do you want the changes in that PR?  If so, do you prefer the use
 of TARGET_POSIX as a runtime variable or listing each TARGET_OS separately?
I know there's some controversy in that thread, I guess I need to check in.
May 13 2016
prev sibling next sibling parent Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On 10/05/2016 12:12, Tomer Filiba wrote:
 Alternatively, an isVersion(x) predicate that I could use in a static if
 could do the trick -- although I think ``version(x || y)`` is more
 readable than ``static if (isVersion(x) || isVersion(y))``
This is actually longer for 2 arguments, but can scale better: import std.meta : anySatisfy; static if (anySatisfy!(isVersion, "assert", "unittest")) ... (There's allSatisfy for x && y).
May 10 2016
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 10 May 2016 at 11:12:58 UTC, Tomer Filiba wrote:
 Hey guys,

 Looking at our code base (weka), I realized it would be really 
 useful if we had logical operators (negation, conjunction, 
 disjunction) in the version's "condition" clause. Here's what I 
 have in mind:

 version(!extra_checks) {
     ...
 }

 version(dlang_ver_2069 || dlang_ver_2070) {
     ...
 }

 Today we have lots of ugly code like

 version(unittest) {} else { ... }

 and bad-old copy-paste for the logical-or case I mentioned.

 Is there any reason for that versions can't take compound 
 logical conditions? Alternatively, an isVersion(x) predicate 
 that I could use in a static if could do the trick -- although 
 I think ``version(x || y)`` is more readable than ``static if 
 (isVersion(x) || isVersion(y))``

 I think it could be a useful feature. Any thoughts?

 -tomer
Still holding out hope Walter will change his mind here.. His rationale is based on keeping code clean and organized, but given that D has no preprocessor macros, it will never look as bad as C++ code, or even close. Compounding versions in C++ accounts for a very small portion of the mess. When you have an #if VERS every 5 lines, however, you've got a problem...but then, you can do that with D's version just the same. The real solution is to properly encapsulate system-dependant code so that whatever version statement you need, is all in one place. Also when you have to hack 10 different macros together to invent a feature your language doesn't have, you've got a problem, but this is much less of a concern in D than C++. When even D gurus writing D compilers have to hack solutions together with static if to get by, its time to re-evaluate the situation. Bit
May 11 2016
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 5/11/2016 10:02 AM, bitwise wrote:
 When even D gurus writing D compilers have to hack solutions together with
 static if to get by,
Really? There are a few lines of that left in dmd, but as a result of mechanical conversion from C++. There's other C++ cruft in there, too.
 its time to re-evaluate the situation.
Actually, dmd is a nice example of how unnecessary it is. The dmd C++ source code used to be full of it.
May 11 2016
parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Wednesday, 11 May 2016 at 20:37:35 UTC, Walter Bright wrote:
 Actually, dmd is a nice example of how unnecessary it is. The 
 dmd C++ source code used to be full of it.
I'm convinced that you're argument is reasonable if version is only for things like platforms, but it's used for a lot of other stuff e.g. version(PrintSomeExtraInfo) or version(CacheSomeStuffForPerformance). Do you think the same principle applies there?
May 11 2016
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/11/2016 1:42 PM, John Colvin wrote:
 I'm convinced that you're argument is reasonable if version is only for things
 like platforms, but it's used for a lot of other stuff e.g.
 version(PrintSomeExtraInfo) or version(CacheSomeStuffForPerformance). Do you
 think the same principle applies there?
Yes, I do. https://github.com/dlang/dmd/pull/5767 I've also seen very good programmers find 'absolutely must have' uses for static if, used them, and caused bugs due to the tangled up nature of it. I had to go in and fix it. It takes a while to change one's thinking from the C++ #if expressions, but the result is worth it.
May 11 2016