Jump to page: 1 2
Thread overview
Version block "conditions" with logical operators
May 10, 2016
Tomer Filiba
May 10, 2016
Joakim
May 10, 2016
Tomer Filiba
May 10, 2016
Tomer Filiba
May 11, 2016
Artur Skawina
May 10, 2016
Johan Engelen
May 10, 2016
Tomer Filiba
May 12, 2016
Joakim
May 12, 2016
Walter Bright
May 13, 2016
Joakim
May 13, 2016
Walter Bright
May 10, 2016
Nick Treleaven
May 11, 2016
bitwise
May 11, 2016
Walter Bright
May 11, 2016
John Colvin
May 11, 2016
Walter Bright
Dec 14, 2018
Andrew Pennebaker
Dec 14, 2018
Jonathan M Davis
May 10, 2016
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
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
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
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
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
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
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
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 11, 2016
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
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
« First   ‹ Prev
1 2