February 11, 2009

Walter Bright wrote:
> Jason House wrote:
>> Would you be willing to introduce an alternative to /+ +/ which would
>> be treated differently by the D1 and D2 compilers? Here are some
>> examples with no attempt at creativity:
>> beginD1 endD1
>> D1 D1 (works like string delimiters)
>> /D2 D2/
> 
> That is an interesting idea. I never thought of that.

What about this?

/+{{VersionIdentifier}} ... +/

The lexer doesn't have to know whether a particular version identifier is defined or not.  That said, I don't know whether you could keep a "sort-of comment" around in the AST until the semantic pass; haven't had much luck deciphering the compiler yet. :P

Plus, it's backwards-compatible! :D

As for what's allowed in the {{..}}, you could always restrict it to a set of version identifiers that represent syntax-incompatible changes.

  -- Daniel
February 11, 2009
"Walter Bright" <newshound1@digitalmars.com> wrote in message news:gmtatl$12om$2@digitalmars.com...
> Nick Sabalausky wrote:
>> The point is, the current semantics for D's version() are *plenty* susceptible to most of same versioning mess as C's #if/#ifdef, and in some cases (such as ||), even worse. With either style, the solution is exactly the same as any other chunk of messy code: Clean it up! Not only is gimping the version-control mechanism the wrong solution, it doesn't even solve the problem anyway.
>
> I'll argue that I've never seen anyone create such a mess in D, while I see it regularly in C.

Agreed, but I'm certain that a significant reason for that is the fact that (and I mean no disrespect to D by this) C has had far more people working on a far larger number of projects that each span far more platforms all for a far longer time. Just look at all of the platforms supported by that GC example. Even the D language itself isn't that multi-platform yet. And then there's all of that GNU C code that's been around much longer than even D itself.

If D had the ubiquity of C (as I hope someday it will), then D would have plenty of mess too. Not as much, granted (for reasons I argue below), but still a fair amount.

> So something about D is discouraging developing those things.
>
> I think the tipping point is that it's too easy in C to slip into writing such a mess without actually trying to, while in D you have to work harder to do it.

I agree there are things about D that discourage such messy tactics, but I don't think it's the lack of !, ||, && and expression-level usage. I think, as Steven said, that the discouragement comes more from version() being a syntactic/semantic part of the language itself instead of a blunt text-replacement engine that neither knows nor cares what it tramples over. That's why you have to work harder to make a conditional-compilation mess, not because it lacks a few features.

Plus, the C preprocessor's syntax automatically invokes a mental association with "blunt text-editing tool" and I think that's a big part of why C coders slip into the mess. D's version() and static if() *look* like an ordinary if(), so that alone is going to decrease urges to use them like a C preprocessor.


February 11, 2009
Steven Schveighoffer wrote:
> That example has nothing to do with || being allowed, and nothing to do with your exhibit B.  Your simple example makes sense to split into multiple files, but you simply waved your magic "split into multiple files" wand at Hans' code and because of the simplicity of D's versioning system, it's more maintainable?  I find it hard to believe without proof.

I cannot prove to you in any mathematical sort of way. All I can say is that I have a lot of experience doing things both ways, and what the results are. I can readily believe that it isn't obvious until one has done it both ways; programming is full of examples of such. Such as:

#define BEGIN {
#define END }

I can't prove that's a bad idea, either. But experience shows it is <g>.


> Besides, your example exists *without* any new features to the versioning system.  That is, your example just shows how it's easier to maintain if you put the version identifier at the outermost layer, instead of in the innermost layer, and I agree with you there.  I just want to try and get rid of the cruft of:
> 
> version(A)
>    version = AorB;
> version(B)
>    version = AorB;

I think I've been misunderstood here. I never intended one to actually use the "or" string in a version, my intent was that one determine just what the feature is that is AorB, and name the identifier that. For example:

version (linux)
	version = Pthreads;
version (OSX)
	version = Pthreads;

version (Pthreads)
	...
else version (Windows)
	...

I don't consider that cruft, but very reasonable self-documentation.


> If you want to prove how much easier it is, actually split Hans' rat's nest into multiple files how you would do it, and I'll show you that it isn't any harder to maintain if you use ||.

I already did port it once, many years ago, it's why I know how hard it is. Take a look at D's gc which uses personality modules. I ported that to the mac pretty easily and never touched the main code. I can't even figure out where the mac osx code is in the hbgc or what would need to be changed for it. I couldn't even figure out if the hbgc supported OSX or not.

To sum up, I've done it both ways. The personality module method approach is head and shoulders the clear winner.
February 11, 2009
Steven Schveighoffer wrote:
> And when the rats nest MUST be created?  Why make it more painful?  I'm sure if Hans Bohem could write it in a clearer fashion he would have.

Hans is in a trap with it. If he reengineered it, he very likely would break many platforms, and he does not have those platforms and has no way to test. The code is very brittle, and it would take a lot of guts to try and reorganize it.

The hbgc is a collection of code submitted by many, many people.


> The same arguments could be said about actual if statements in code, yet you have no qualms not forcing people to combine their logic into boolean variables before using an if statement.

It's not the same, because versions tend to slice through whole programs in an unstructured manner, while garden variety if statements are structured and local.
February 11, 2009
"Daniel Keep" <daniel.keep.lists@gmail.com> wrote in message news:gmtf5g$1ban$1@digitalmars.com...
>
> --version=OhGodMakeItStop=false
>

lol :)

I love all of the contrived code we come up with on this newsgroup. Other gems just from this "Why version()?" thread alone:

version=canBeUsedToPickupChicks
version(Poopy) {} else

> You might be able to rework the syntax so that you can define the default value of a version symbol in a module, then override it from the command line.
>
> module simd_versions;
> version UseMMX=false, UseSSE=false, UseSSE2=false, ...;
>

I think you've just demonstrated here that this feature *can* be made to work.


February 11, 2009
Ary Borenszweig escribió:
> Jason House escribió:
>> Walter Bright Wrote:
>>
>>   
>>> 5. Why can't one 'version out' syntax that is not recognized by the compiler?
>>>
>>> The problem is that supporting this requires semantic analysis in order to successfully lex and parse the source code. Breaking this will make the lexing and parsing an order of magnitude harder for third party tools to do. If you need to 'comment out' a section of syntactically invalid code, use the /+ ... +/ nesting comment.
>>
>> Would you be willing to introduce an alternative to /+ +/ which would be treated differently by the D1 and D2 compilers? Here are some examples with no attempt at creativity:
>> beginD1 endD1
>> D1 D1 (works like string delimiters)
>> /D2 D2/
> 
> Why not have special versions like D1, D2, D3 for the compiler to treat them differently? If a version(DX) is found that is not the current's compiler version, it just consumes tokens (counting opening and closing of brackets, etc.) until the curly's close. Much like the asm statement.

Ah, mmm... the lexer also needs to know this. Doesn't work. :-(

Well, it could work if you lookahead a few tokens...
February 11, 2009
Derek Parnell wrote:
> The fine line that divides when to duplicate and when not to duplicate, is
> hard to see clearly. I tend to favour the less duplication approach, but
> only when it leads to lower maintenance costs.

I agree it's hard to see clearly, and often can only be seen after attempting a couple of ports.
February 11, 2009
Andrei Alexandrescu wrote:
> Steven Schveighoffer wrote:
>> "Walter Bright" wrote
>> Think of code that is versioned around architecture that would look horrendous if you have to do version statements that have all different combinations of stuff.  If I have 5 different architectures to support, I don't want to have to define a module that has 2^5 different version combinations.  On top of that, the way versioning works, you need 2^4 * 5 different statements:
>>
>> version(A)
>> {
>>    version = AorB;
>>    version = AorC;
>>    version = AorD;
>>    version = AorE;
>>    version = AorBorC;
>>    version = AorBorD;
>>    // ad nauseum.
>> }
>> version(B)
>> {
>>    // oh fun! let's do it again!!!!
>>    version = AorB;
>>    version = BorC;
>>    version = BorD;
>>    ...
>> }
>>
>> When I add another architecture, *gasp* I have to double the statements (to do them now with and without version(F) ), and now I have to do another 2^5 statements for the version(F) block.  Wheee!
> 
> But this is clearly the wrong way of cutting the pie. What you need is define features that are supported by some of the versions. You don't need to judge in terms of logical operators between versions.
> 
> version (A) version = canBeUsedToPickupChicks;
> version (C) version = canBeUsedToPickupChicks;
> // version (B) no good

There's one slightly irritating issue I've run into with versions recently.  In the spare few instances where I actually need to do something like this, I like to put everything together so I know at a glance what's going on.  However, versions may not be defined in class scope, so:

class C
{
    version (A) version = useSomeFancyThing;
    version (B) version = useSomeFancyThing;
    version (useSomeFancyThing)
    {
        Fancy[1024] fancyPants;
    }
}

This is illegal.  Kind of a small issue, but I grumbled about it for a minute or two before I broken down and just duplicated the declaration for each version.  I could have easily put the tests at module scope before the class declaration, but I wanted everything together.


Sean
February 11, 2009
Nick Sabalausky wrote:
> I have to admit, that's a very compelling example. I hadn't thought of anything like that.

I didn't either, until I got bit by it <g>.


> I guess the moral is "DRY is normally great, but be very careful with it when using it across multiple builds." But I'm still not sure we should be going that far. For instance:
> 
> enum
> {
>     version(Linux || OSX)
>     {
>         O_RDONLY = 0,
>         O_WRONLY = 1,
>         O_RDWR = 2,
>         O_CREAT = 0100,
>     }
>     version(Linux)
>     {
>         O_APPEND = 02000,
>     }
>     version(OSX)
>     {
>         O_APPEND = 8,
>         O_SYMLINK = 0x200000,
>     }
> }
> 
> That seems reasonable to me.

Yeah, but I guarantee you that there will be some "common to all" ones and an irresistible temptation to put them in outside any version block. I also like to have all the enums for one platform in one list, not broken up by other stuff for other platforms, because then it is easier to verify against the C headers.


> Besides, without expression-level version(), the project's author might just as likely think "Shoot, this is stupid that D doesn't let me make this nicely DRY. But I really like DRY, so I guess I'll just resort to an external C-like pre-processor".

There's something wrong with that option, because although it's always been on the table (not just for D, but for any language) nobody ever uses it.

Back in the '80s people would often complain about the limitations of the C preprocessor, and the answer was always "if you want a real macro preprocessor, use m4". But I've never heard of anyone ever actually using m4 to preprocess C. The "just use m4" meme became a joke, like Bob Villa saying "just use track lighting".
February 11, 2009
Hello Walter,

> and it works great for linux. Now I port my code to OSX, and it
> mysteriously dies. After much frustration, I discover that O_APPEND
> for OSX is 8, not 02000. (Yes, this kind of thing has happened to me.
> I only found it by suspecting the problem, and then going through
> every fscking definition comparing it to the one in the system .h
> file. Yeech.)

A little fun with a C compiler/preprocessor and you could make a C file that gives you the value by name. Then a bit of fun with D unittest that call into the C code and you could have a system to check if something got messed up for the next OS you port to.