www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - FYI my experience with D' version

reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Jul 28 2012
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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....
Jul 29 2012
parent Jacob Carlborg <doob me.com> writes:
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
Jul 29 2012
prev sibling next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
 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?
Jul 30 2012
prev sibling next sibling parent reply torhu <no spam.invalid> writes:
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.
Jul 30 2012
parent reply Jacob Carlborg <doob me.com> writes:
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
Jul 30 2012
parent Don Clugston <dac nospam.com> writes:
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.
Jul 30 2012
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Jul 30 2012
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Jul 30 2012