Thread overview
FYI my experience with D' version
Jul 28, 2012
Adam D. Ruppe
Jul 29, 2012
Adam D. Ruppe
Jul 29, 2012
Jacob Carlborg
Jul 30, 2012
Adam D. Ruppe
Jul 30, 2012
Tobias Pankrath
Jul 30, 2012
torhu
Jul 30, 2012
Jacob Carlborg
Jul 30, 2012
Don Clugston
Jul 30, 2012
Adam D. Ruppe
July 28, 2012
After some experience, I've come to the conclusion that
using D's version() with custom things is usually a mistake.
Not always - I think it is still good for platform like tweaks,
version(X86) or version(Windows), or even version(Custom_Library),
(note, it is often smart to put an "else static assert(0)" at the
end of platform version lists, so it doesn't pass silently on new one)

But using it to enable or disable particular named features or
other kind of custom configuration things is a mistake. Every
time I've done it for that, I've gone back and changed it.


The reason is it is too easy to get it wrong:

module a:
version = Foo;

module b:
import a;
version(Foo) { this doesn't actually execute }


You intuitively think it would work, but it doesn't,
and it isn't an error either, so you might not notice
until you have a live bug. (This has happened to me more
than once!)



So, the question is: how do we do this kind of thing? The
solution I like best right now is to use config modules and
static if with enums. version() itself is rarely seen.

We can take advantage of the fact that D modules don't have
to match the filename to make a manageable build process:


acme_config.d
---
module application.config;

enum name = "Acme Corp.";
enum bool wantsFeatureX = false;
---

dm_config.d
---
module application.config;

enum name = "Digital Mars";
enum bool wantsFeatureX = true;
---


app.d
---
module application.app;

import application.config;

void main() {
   import std.stdio;
   writeln("Hello, ", name);
   static if(wantsFeatureX)
      writeln("You have purchased feature X!");
}
---



Now, when building the app, you just change the filename
listed for the config.

Make Acme's binary:

dmd app.d acme_config.d

Make the other one:

dmd app.d dm_config.d



and it works. Benefits include:


* All the config is centralized in a file

* The config is completely isolated from the app itself; if Acme buys a source license, they won't see any version(digitalmars) crap sprinkled about.

* Compile errors if you mess up a condition's name

* Can also be used for other customizations, including the company name here, but also things like defining a class to override some methods.



The biggest downside is if you add a function, you have to go back
to ALL the config files and add it in to make the project compile.

enum bool wantsNewFeature = false;

which can be a hassle if you have like 20 config files. But, this
is good and bad because it does make you think about it at least,
not just silently doing the wrong thing if you forget.
July 29, 2012
I just hit another downside with custom version(): two libraries
might use the same name to mean different things.

The version has no namespacing, so that's another reason for not
using it for custom things. The config modules might be the best idea
for libraries too. I wonder if we could do a default module that is
easily overridden....
July 29, 2012
On 2012-07-29 18:42, Adam D. Ruppe wrote:
> I just hit another downside with custom version(): two libraries
> might use the same name to mean different things.
>
> The version has no namespacing, so that's another reason for not
> using it for custom things. The config modules might be the best idea
> for libraries too. I wonder if we could do a default module that is
> easily overridden....

You could use versions which sets bool variables and use static-if. I don't know if that's any better.

-- 
/Jacob Carlborg
July 30, 2012
> The biggest downside is if you add a function, you have to go back
> to ALL the config files and add it in to make the project compile.
>
> enum bool wantsNewFeature = false;
>
> which can be a hassle if you have like 20 config files. But, this
> is good and bad because it does make you think about it at least,
> not just silently doing the wrong thing if you forget.

Couldn't you make a template mixin that inserts default values if not yet defined?
July 30, 2012
On 28.07.2012 22:55, Adam D. Ruppe wrote:
> After some experience, I've come to the conclusion that
> using D's version() with custom things is usually a mistake.
> Not always - I think it is still good for platform like tweaks,
> version(X86) or version(Windows), or even version(Custom_Library),
> (note, it is often smart to put an "else static assert(0)" at the
> end of platform version lists, so it doesn't pass silently on new
> one)
>
> But using it to enable or disable particular named features or
> other kind of custom configuration things is a mistake. Every
> time I've done it for that, I've gone back and changed it.
>
>
> The reason is it is too easy to get it wrong:
>
> module a:
> version = Foo;
>
> module b:
> import a;
> version(Foo) { this doesn't actually execute }

version is good for global options that you set with -version on the command line.  And can also be used internally in a module, but doesn't work across modules.  But it seems you have discovered this the hard way already.

I think there was a discussion about this a few years ago, Walter did it this way on purpose.  Can't remember the details, though.
July 30, 2012
On 2012-07-30 12:30, torhu wrote:

> version is good for global options that you set with -version on the
> command line.  And can also be used internally in a module, but doesn't
> work across modules.  But it seems you have discovered this the hard way
> already.
>
> I think there was a discussion about this a few years ago, Walter did it
> this way on purpose.  Can't remember the details, though.

He probably wants to avoid the C macro hell.

-- 
/Jacob Carlborg
July 30, 2012
On 30/07/12 14:32, Jacob Carlborg wrote:
> On 2012-07-30 12:30, torhu wrote:
>
>> version is good for global options that you set with -version on the
>> command line.  And can also be used internally in a module, but doesn't
>> work across modules.  But it seems you have discovered this the hard way
>> already.
>>
>> I think there was a discussion about this a few years ago, Walter did it
>> this way on purpose.  Can't remember the details, though.
>
> He probably wants to avoid the C macro hell.
>


IIRC it's because version identifiers are global.

______________________________
module b;
version = CoolStuff;
______________________________

module a;
import b;
version (X86)
{
   version = CoolStuff;
}

version(CoolStuff)
{
   // Looks as though this is only true on X86.
   // But because module b used the same name, it's actually true always.
}
______________________________

These types of problems would be avoided if we used the one-definition rule for version statements, bugzilla 7417.














July 30, 2012
On Monday, 30 July 2012 at 10:30:12 UTC, torhu wrote:
> I think there was a discussion about this a few years ago, Walter did it this way on purpose.  Can't remember the details, though.

Hell, that might have been me. I first realized version doesn't
work for what I was using it for a couple years back.

Since then, I've been trying to nail down what it is good
for and alternatives for the config stuff I used to use it
for.

This thread is basically me sharing what I've learned so far.
July 30, 2012
On Sunday, 29 July 2012 at 19:20:30 UTC, Jacob Carlborg wrote:
> You could use versions which sets bool variables and use static-if. I don't know if that's any better.

It'd still have the problem of the global name. Consider:

-version=use_foo

but then lib a and lib b both have:

version(use_foo)
  bool usingFoo = true;


but they have different meanings of what "foo" mean. The
bool there won't trample cross module, but the version will
still set it inappropriately.


I really think using version() for features is just always
a mistake since these things do happen anyway. The only place
where it is clear is things like platform versions.