Good examples of version() algebra in real code

Hipreme msnmancini at hotmail.com
Mon May 22 11:06:33 UTC 2023


On Monday, 22 May 2023 at 08:21:50 UTC, Walter Bright wrote:
> On 5/21/2023 7:01 PM, Hipreme wrote:
>> Right now I've come to understand that using feature based 
>> versions instead of real versions really makes a lot of 
>> difference. But as Guillaume pointed out, there is still this 
>> other problem of defining a feature based on multiple 
>> platforms and this solution doesn't really make one write a 
>> lot less, so I still find this solution lacking for our 
>> problem which makes me wonder if there really exists a good 
>> solution for that.
>
> version(linux)         enum ExtraFunctionality = true;
> else version(OSX)      enum ExtraFunctionality = true;
> else version(Windows)  enum ExtraFunctionality = false;
> else static assert(0, "system not accounted for");
>
> The static assert is there because a very common failure of 
> #ifdef hell is to have defaults that botch things up when a new 
> version is added.
>
> This happens sometimes in the druntime imports, when I discover 
> them I add the static assert.
>
> There are still other ways to do it:
>
> ```
> import extra;
> void foo()
> {
>     extraFunctionality();
> }
> ```
>
> ```
> module extra;
>
> void extraFunctionality()
> {
>   version(linux)         doit();
>   else version(OSX)      doit();
>   else version(Windows)  { }
>   else static assert(0, "system not accounted for");
> }
> ```
>
> Another way is to write a "personality module" for each 
> operating system, and then import the right one:
>
> ```
> module extra;
> version (linux) import extraLinux;
> else version (OSX) import extraOSX;
> ... and so on ...
> ```
>
> Personally, I like to make the core code version-independent 
> and OS-independent and hide the variances in separate modules. 
> Isn't foo() clean looking?

Yes, I do understand. Although I prefer `static assert` to not be 
used, but the runtime `assert`. I have done a port of the 
druntime and the `static assert` usage really is a pain since 
there is just a plain lot of code which is not used by me, but 
only for its existence, it causes a compilation error.


I think the feature based is cleaner to read most of the time 
(and scalable), I have done a good refactor in a lot of D code 
already using that, and IMO, it did done wonders into making the 
code intention crystal clear even for non maintainers. This is a 
thing which I've come to understand the decision of not allowing 
boolean operators on `version`. Maybe if there was a construct 
for allowing **only version declaration** with boolean operators 
like:
`version RelaxedSystems = version(Windows && linux && !OSX)` (it 
would not be global as the `version` is right now. i.e: not allow 
this syntax to be used standalone.

So, the operators aren't really the hell that causes the `#ifdef` 
hell as you said. The problem are 2:

1: They being defined over all files. While trying to port 
newlibc, I've come to find a type to be defined over 6 files. 
Which meant I really went jumping from a file to file until I was 
able to find how the type were defined. This is a real problem 
since the type is not self contained so it is super hard to look. 
How to solve that: D has solved! Just make the `version =` not 
spam into multiple files.

2: Operators: they don't really make sense when other people are 
looking into them, which is solved by having the feature named, 
so, enforcing the naming to use the operators could be a problem 
solving in the syntax. Since they aren't global, they are painful 
to keep writing all the time the same thing (I've tried doing 
that on directx-d binding and omg, I basically got a 8 line 
headers in almost all files, sure, it is easier to read than C, 
but it was painful writing them).



More information about the Digitalmars-d mailing list