www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Conditional compilation inside asm and enum declarations

reply "Julian Salazar" <julian ifeelrandom.com> writes:
Hi, I'm new here to the community but I've been using D for a while now, and 
I have to say that it's a great programming language. I'd like to get 
involved in this community and help shape this language.

I'm just wondering about a minor issue: why are conditional blocks invalid 
within expressions such as enum and asm? I mean, in trivial cases it's fine, 
but in instances where code duplication is a big maintainability nightmare, 
making conditional compilation more flexible would have benefits for 
developers.

Something like (I know it's a trivial example, but you get the point):

asm {
    version(x86) mov EAX, 1;
    else version(x86_64) mov EAX, 2;
}

would trigger an error. Also, though I know enum qualifies as a 
constant/datatype cross, structs and classes are perfectly fine with 
conditional compilation. Couldn't the lexical stuff be changed to support it 
for enum and asm as well?

Also, I noticed that there is no formal specification page for x86-64 inline 
assembly. You define a predefined version identifier such as 
D_InlineAsm_X86_64, but you don't define registers and instructions 
pertaining to it. In GDC for example, using the RAX register in the D inline 
ASM syntax is invalid. Not sure what the case is in LDC (they probably do 
implement it for x86-64), and I know DMD does not have a 64-bit version, but 
the spec should at least have a definition for compilers that do implement 
64-bit support.

Thanks for your time,
- Julian 
Jul 13 2009
next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Mon, Jul 13, 2009 at 11:15 AM, Julian Salazar<julian ifeelrandom.com> wr=
ote:
 Hi, I'm new here to the community but I've been using D for a while now, =

 I have to say that it's a great programming language. I'd like to get
 involved in this community and help shape this language.

Man, so would I ;)
 I'm just wondering about a minor issue: why are conditional blocks invali=

 within expressions such as enum and asm? I mean, in trivial cases it's fi=

 but in instances where code duplication is a big maintainability nightmar=

 making conditional compilation more flexible would have benefits for
 developers.

 Something like (I know it's a trivial example, but you get the point):

 asm {
 =A0 version(x86) mov EAX, 1;
 =A0 else version(x86_64) mov EAX, 2;
 }

 would trigger an error. Also, though I know enum qualifies as a
 constant/datatype cross, structs and classes are perfectly fine with
 conditional compilation. Couldn't the lexical stuff be changed to support=

 for enum and asm as well?

That'd be nice. If you'd like, you could file an enhancement in D's bugzilla, at http://d.puremagic.com/issues/.
 Also, I noticed that there is no formal specification page for x86-64 inl=

 assembly. You define a predefined version identifier such as
 D_InlineAsm_X86_64, but you don't define registers and instructions
 pertaining to it. In GDC for example, using the RAX register in the D inl=

 ASM syntax is invalid. Not sure what the case is in LDC (they probably do
 implement it for x86-64), and I know DMD does not have a 64-bit version, =

 the spec should at least have a definition for compilers that do implemen=

 64-bit support.

I'm pretty sure LDC does implement x64 inline assembly. It doesn't seem to be documented yet. GDC supports inline assembly for just about any platform but with a nonstandard GCC-based syntax; see the "Extended Assembler" section here: http://dgcc.sourceforge.net/gdc/manual.html I've personally used it for x64 assembly with great success :)
Jul 13 2009
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Julian Salazar wrote:
 Hi, I'm new here to the community but I've been using D for a while now, 
 and I have to say that it's a great programming language. I'd like to 
 get involved in this community and help shape this language.

Great! This is the place to effect that, and the improvement related to allowing version inside asm blocks is a good start. Andrei
Jul 13 2009
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Julian Salazar wrote:
 Hi, I'm new here to the community but I've been using D for a while now, 
 and I have to say that it's a great programming language. I'd like to 
 get involved in this community and help shape this language.

Welcome!
 I'm just wondering about a minor issue: why are conditional blocks 
 invalid within expressions such as enum and asm? I mean, in trivial 
 cases it's fine, but in instances where code duplication is a big 
 maintainability nightmare, making conditional compilation more flexible 
 would have benefits for developers.

The request to do it for enums has been thrashed about before. Essentially, version works at the declaration and statement level, not at the expression level or in between tokens, etc. The reason for this is to encourage a more modular approach to versioning than the typical C method of doing it at the lowest level.
 Something like (I know it's a trivial example, but you get the point):
 
 asm {
    version(x86) mov EAX, 1;
    else version(x86_64) mov EAX, 2;
 }
 
 would trigger an error. Also, though I know enum qualifies as a 
 constant/datatype cross, structs and classes are perfectly fine with 
 conditional compilation. Couldn't the lexical stuff be changed to 
 support it for enum and asm as well?

Let me illustrate by a current example. The linker (optlink) is written 100% in assembler. This makes it rather intractable. It's also loaded up with line-by-line nested conditional assembly (and a lot of macros). It's so hard to see what is *actually* being compiled that I'll assemble it, run OBJ2ASM on the output, and work off of the disassembled code. So, in essence, the idea is to push conditional compilation to higher levels, not lower levels. Ideally, versioning would be done by abstracting all the version differences into an interface implemented by different modules.
 Also, I noticed that there is no formal specification page for x86-64 
 inline assembly. You define a predefined version identifier such as 
 D_InlineAsm_X86_64, but you don't define registers and instructions 
 pertaining to it. In GDC for example, using the RAX register in the D 
 inline ASM syntax is invalid. Not sure what the case is in LDC (they 
 probably do implement it for x86-64), and I know DMD does not have a 
 64-bit version, but the spec should at least have a definition for 
 compilers that do implement 64-bit support.

The first approximation to the definition is to use the Intel asm syntax as outlined in their processor data sheets. I haven't written a spec more detailed than that because it's a lot of work and I'm lazy, and such work is not terribly exciting. But if you'd like to help with that, I'd welcome it.
Jul 13 2009
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:
 The first approximation to the definition is to use the Intel asm syntax 
 as outlined in their processor data sheets. I haven't written a spec 
 more detailed than that because it's a lot of work and I'm lazy, and 
 such work is not terribly exciting. But if you'd like to help with that, 
 I'd welcome it.

That can be done keeping a close look at how LDC has done things. Bye, bearophile
Jul 13 2009
prev sibling next sibling parent reply "Julian Salazar" <julian ifeelrandom.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:h3fu36$22hl$1 digitalmars.com...
 Welcome!

A welcome from the guy who actually created the D language - thanks!
 The request to do it for enums has been thrashed about before. 
 Essentially, version works at the declaration and statement level, not at 
 the expression level or in between tokens, etc. The reason for this is to 
 encourage a more modular approach to versioning than the typical C method 
 of doing it at the lowest level.

That makes sense. I realize that enums aren't structures of data per se, but simply integers with a range of defined values, which of course should defined and parsed as one expression. And the comma separator in a list doesn't lend itself well to being separated by different expressions (in fact, I recently filed issue 3063 where DSSS goes out-of-memory because of an extra comma in D2's std.dateparse - not your fault but it'd be nice to use DSSS again without editing the library source).
 Let me illustrate by a current example. The linker (optlink) is written 
 100% in assembler. This makes it rather intractable. It's also loaded up 
 with line-by-line nested conditional assembly (and a lot of macros). It's 
 so hard to see what is *actually* being compiled that I'll assemble it, 
 run OBJ2ASM on the output, and work off of the disassembled code.

Maybe it's time for some maintenance? Or a rewrite in a certain higher-level language? ;)
 So, in essence, the idea is to push conditional compilation to higher 
 levels, not lower levels. Ideally, versioning would be done by abstracting 
 all the version differences into an interface implemented by different 
 modules.

You make a valid point. However the issue still remains where two versions for example are inextricably linked. It's understandable that you would keep separate modules when compiling between architectures like x86 and SPARC64, or even Basic and Advanced versions (which usually just involve including functionality). However, when it involves situations such as the x86 & x86-64 which ARE very similar platforms and a full copy with minimal rewrite does not seem justified. Tell that to device driver writers with their C, inline assembly and #ifdefs. (Side note: I've worked around the versioning with a slightly more cumbersome syntax. asm { ...code... } // But I can't assure that no code is compiled between the two asm statements =/ version(x86) asm { ...code... } else version(x86_64) asm { ...code... } )
 The first approximation to the definition is to use the Intel asm syntax 
 as outlined in their processor data sheets. I haven't written a spec more 
 detailed than that because it's a lot of work and I'm lazy, and such work 
 is not terribly exciting. But if you'd like to help with that, I'd welcome 
 it.

How would I go about doing that? It seems like all the work that remains to be done is just updating opcodes and the valid registers. Maybe a bit of tightening of the specification and syntax, but other than that the basic outline is there. Thanks to everyone for the positive response!
Jul 13 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Julian Salazar wrote:
 Maybe it's time for some maintenance? Or a rewrite in a certain 
 higher-level language? ;)

Yes, but to rewrite it requires understanding it, and that means obj2asm.
 So, in essence, the idea is to push conditional compilation to higher 
 levels, not lower levels. Ideally, versioning would be done by 
 abstracting all the version differences into an interface implemented 
 by different modules.

You make a valid point. However the issue still remains where two versions for example are inextricably linked. It's understandable that you would keep separate modules when compiling between architectures like x86 and SPARC64, or even Basic and Advanced versions (which usually just involve including functionality). However, when it involves situations such as the x86 & x86-64 which ARE very similar platforms and a full copy with minimal rewrite does not seem justified. Tell that to device driver writers with their C, inline assembly and #ifdefs. (Side note: I've worked around the versioning with a slightly more cumbersome syntax. asm { ...code... } // But I can't assure that no code is compiled between the two asm statements =/ version(x86) asm { ...code... } else version(x86_64) asm { ...code... } )

It's been rehashed here several times (not to rag on you, just to point out that it isn't something that's been overlooked). To sum up, I've worked a lot with both styles - #ifdef, and separating dependencies into independent modules. The latter works a lot better. I know it's hard to believe if you're used to the #ifdef style. I've been doing some work to remove #ifdef's from the dmd compiler source, and the results so far have been very satisfactory.
 The first approximation to the definition is to use the Intel asm 
 syntax as outlined in their processor data sheets. I haven't written a 
 spec more detailed than that because it's a lot of work and I'm lazy, 
 and such work is not terribly exciting. But if you'd like to help with 
 that, I'd welcome it.

How would I go about doing that? It seems like all the work that remains to be done is just updating opcodes and the valid registers. Maybe a bit of tightening of the specification and syntax, but other than that the basic outline is there.

For one thing, the valid registers for x86 mode do not include the 64 bit registers. There's also the grammar for the operands, which is not specified in the D spec.
Jul 13 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Mon, Jul 13, 2009 at 10:05 PM, Walter
 Bright<newshound1 digitalmars.com> wrote:
 Julian Salazar wrote:
 It's been rehashed here several times (not to rag on you, just to point out
 that it isn't something that's been overlooked). To sum up, I've worked a
 lot with both styles - #ifdef, and separating dependencies into independent
 modules. The latter works a lot better. I know it's hard to believe if
 you're used to the #ifdef style. I've been doing some work to remove
 #ifdef's from the dmd compiler source, and the results so far have been very
 satisfactory.

But from where I sit it looked like Walter didn't really convince anyone. To me this seems like a point where D is overly patronizing, to use the phrase from a recent post.

FWIW I've recently experimented in Phobos with function-level versioning, e.g.: version(Posix) void setenv(in char[] name, in char[] value, bool overwrite) { ... } version(Windows) void setenv(in char[] name, in char[] value, bool overwrite) { ... } Then I have the function right there with all versioned implementations. To me that seems better than Phobos' existing style of version'ing large portions of code, which inevitably results in duplicating a lot of the functionality in two places. One other thing I like about the approach above is that it does not necessitate an extra level of indentation, and it doesn't make you feel guilty for not adding it. I cringe whenever I see at the top level: version (something) { non_indented_stuff more_non_indented_stuff etc } Andrei
Jul 14 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Andrei Alexandrescu wrote:
 Then I have the function right there with all versioned implementations. 
 To me that seems better than Phobos' existing style of version'ing large 
 portions of code, which inevitably results in duplicating a lot of the 
 functionality in two places.

I wouldn't consider Phobos to be an exemplary example of how to do versioning, though it should be. I think much of it, like std.file, should be split off into os-dependent "personality" modules, much like the os api modules have been.
Jul 14 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Then I have the function right there with all versioned 
 implementations. To me that seems better than Phobos' existing style 
 of version'ing large portions of code, which inevitably results in 
 duplicating a lot of the functionality in two places.

I wouldn't consider Phobos to be an exemplary example of how to do versioning, though it should be. I think much of it, like std.file, should be split off into os-dependent "personality" modules, much like the os api modules have been.

That would exacerbate code duplication. Consider: version(Windows) void[] read(in char[] name) { ... } version(Posix) void[] read(in char[] name) { ... } S readText(S = string)(in char[] name) { ... } In my approach they are laid as you see them, which I find very well-organized. In your approach you'd define several files each specialized for an OS, which would duplicate readText, or put readText into a common file and have it include platform-specific files. Both solutions are unnecessarily complicated to the simple and clear code above. Andrei
Jul 14 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Then I have the function right there with all versioned 
 implementations. To me that seems better than Phobos' existing style 
 of version'ing large portions of code, which inevitably results in 
 duplicating a lot of the functionality in two places.

I wouldn't consider Phobos to be an exemplary example of how to do versioning, though it should be. I think much of it, like std.file, should be split off into os-dependent "personality" modules, much like the os api modules have been.

That would exacerbate code duplication. Consider: version(Windows) void[] read(in char[] name) { ... } version(Posix) void[] read(in char[] name) { ... } S readText(S = string)(in char[] name) { ... } In my approach they are laid as you see them, which I find very well-organized.

There is no duplication in: ============ std.file ======================= version (Posix) import std.file.posix; version (Windows) import std.file.windows; /* code common to both goes here, like readText() */ ============================================= that is not also in the layout you described. But there's no hard and fast rule here, and since you are doing the actual work, I defer to your judgment on those cases.
 In your approach you'd define several files each 
 specialized for an OS, which would duplicate readText, or put readText 
 into a common file and have it include platform-specific files. Both 
 solutions are unnecessarily complicated to the simple and clear code above.

While individual details vary, having personality modules for an os offers some nice advantages: 1. It's pretty clear what is happening for each system. 2. An expert on OSA can work on the OSA implementation without risking breaking the OSB implementation for which he had no expertise. 3. By looking at which files changed, you can tell which OS support got updated and which didn't. 4. Porting to a new platform is easier as you've got a list of personality modules that need to be created, rather than version statements threaded through the file contents. 5. The "else" clause in OS version statements tend to be wrong when porting to a new system, meaning that each version has to be gone through manually - overlooking one doesn't always create an obvious error. I think the std.core.sys.* modules illustrate the advantages nicely, especially considering the former kludge-fest bug-ridden way it was done. The core.stdc.stdio still needs some work in this regard, however.
Jul 14 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Andrei Alexandrescu wrote:
 In my approach they are laid as you see them, which I find very 
 well-organized.

There is no duplication in:

I said there's either duplication or complication.
 But there's no hard and fast rule here, and since you are doing the 
 actual work, I defer to your judgment on those cases.

Great, thanks!
 While individual details vary, having personality modules for an os 
 offers some nice advantages:
 
 1. It's pretty clear what is happening for each system.

Not clear to me. It's happened to me more than once to fix a function without knowing that it's version()ed (your fault: you didn't indent it) and that there's a corresponding Windows function some miles away. To me it's much clearer to have all specializations of a given piece of functionality close to one another. They'd naturally tend to converge, not diverge.
 2. An expert on OSA can work on the OSA implementation without risking 
 breaking the OSB implementation for which he had no expertise.

Ok.
 3. By looking at which files changed, you can tell which OS support got 
 updated and which didn't.

I never look... :o)
 4. Porting to a new platform is easier as you've got a list of 
 personality modules that need to be created, rather than version 
 statements threaded through the file contents.

No. This is where your point gets destroyed. Unittests should dictate what must be done for porting to a new platform. Your approach forces either duplicate unittests, or collector files that add clutter.
 5. The "else" clause in OS version statements tend to be wrong when 
 porting to a new system, meaning that each version has to be gone 
 through manually - overlooking one doesn't always create an obvious error.

I try to avoid else. I want to implement setenv on Windows, I prefix it with Windows. I want to implement it on Posix, I prefix it with Posix. Then I write one unittest. Then when a new OS comes, setenv won't be found so the unittest can't run. Problem solved *much* better.
 I think the std.core.sys.* modules illustrate the advantages nicely, 
 especially considering the former kludge-fest bug-ridden way it was 
 done. The core.stdc.stdio still needs some work in this regard, however.

I don't know about that. Andrei
Jul 14 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Andrei Alexandrescu wrote:
 1. It's pretty clear what is happening for each system.

without knowing that it's version()ed (your fault: you didn't indent it) and that there's a corresponding Windows function some miles away.

That stuff wasn't in personality modules :-)
 To me it's much clearer to have all specializations of a given piece of 
 functionality close to one another. They'd naturally tend to converge, 
 not diverge.

That works fine initially. Things tend to go awry after a while, though. Take a look at the optlink source. I'm sure it didn't start out that way - the convoluted versioning logic was added layer by layer over many years. If you find large swaths of logic are being duplicated in personality modules, that suggests that the wrong level of abstraction is being used. One of the reasons the FreeBSD port went much faster is I did some refactoring of the abstractions when I did the OSX port.
 4. Porting to a new platform is easier as you've got a list of 
 personality modules that need to be created, rather than version 
 statements threaded through the file contents.

No. This is where your point gets destroyed. Unittests should dictate what must be done for porting to a new platform. Your approach forces either duplicate unittests, or collector files that add clutter.

While I'm a great believer in unittests, I don't agree they are the answer to design problems. They are a supplement, not a replacement. I don't think we're anywhere near 100% Phobos unit test coverage yet. Clutter is always a problem that needs to be continuously reviewed.
 5. The "else" clause in OS version statements tend to be wrong when 
 porting to a new system, meaning that each version has to be gone 
 through manually - overlooking one doesn't always create an obvious 
 error.

I try to avoid else. I want to implement setenv on Windows, I prefix it with Windows. I want to implement it on Posix, I prefix it with Posix. Then I write one unittest. Then when a new OS comes, setenv won't be found so the unittest can't run. Problem solved *much* better.

I agree that the else clause should be avoided for such dependencies, but the damn things tend to creep back in :-( and I'm back to manually grepping for versions and ticking them off one by one. I've done the (initial) port to Linux, I've done the ports to OSX, and FreeBSD. It was easier to port the personality modules than the version statements, especially when they went beyond the trivial. It's been easier to do the same with the compiler source code with the personality modules, too. I've had a very positive experience with it.
Jul 14 2009
prev sibling next sibling parent reply "Julian Salazar" <julian ifeelrandom.com> writes:
I appreciate the reply, and I guess that your point about separating 
dependencies into different modules is a good form of version management. 
I'll still be using the version(x86) asm {...} else version(x86_64) asm 
{...} syntax for a while though. But I'm done arguing that point.

My final question is just about how I would go about contributing to the 
Inline Assembler specification. I found that you generate the language spec 
from the trunk/docsrc folder on the Phobos SVN repo on dsource.org. So, am I 
right to think that I could just download the docsrc folder, edit the 
iasm.dd file (and use the linux.mak file to view it in .html) and submit it 
here for review? 
Jul 14 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Julian Salazar wrote:
 My final question is just about how I would go about contributing to the 
 Inline Assembler specification. I found that you generate the language 
 spec from the trunk/docsrc folder on the Phobos SVN repo on dsource.org. 
 So, am I right to think that I could just download the docsrc folder, 
 edit the iasm.dd file (and use the linux.mak file to view it in .html) 
 and submit it here for review?

Right, except that submitting diffs to bugzilla as an enhancement proposal is the best approach. Newsgroups are for transitory discussions.
Jul 14 2009
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 But from where I sit it looked like Walter didn't really convince
 anyone.  To me this seems like a point where D is overly patronizing,
 to use the phrase from a recent post.

You could argue that, but it also took a long time to convince many about the merit of const and immutable. I understand that C style versioning is so seductive, it's very hard to see what's wrong with it. (For another reason against such, I could send you some of the source to optlink. It's chock full of line by line versioning, nested versioning, a couple dozen version arguments, it's so bad the only way I can tell what's going on is to compile it then *disassemble* it to see what the code actually is.) Contrast that with the dmd front end source where I've made a concerted effort (not 100% yet) to remove #ifdef's. And I didn't even touch on what would have to happen if versioning could slice anywhere - it would have to be done as a separate pre-pass. It couldn't be integrated in to the current one-pass parser, and would do a fine job of screwing up syntax highlighters and pretty-printers much like C's preprocessor can.
Jul 14 2009
next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 I do think you make a convincing argument that in general lots of
 micro ifdefs everywhere is not the right approach.
 But I remain unconvinced that potential for abuse is a good reason to
 disallow finer scale version() statements.

It's where the line between micro and fine is that you disagree with, not the principle?
Jul 14 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Walter Bright, el 14 de julio a las 10:01 me escribiste:
 Bill Baxter wrote:
But from where I sit it looked like Walter didn't really convince
anyone.  To me this seems like a point where D is overly patronizing,
to use the phrase from a recent post.

You could argue that, but it also took a long time to convince many about the merit of const and immutable. I understand that C style versioning is so seductive, it's very hard to see what's wrong with it. (For another reason against such, I could send you some of the source to optlink. It's chock full of line by line versioning, nested versioning, a couple dozen version arguments, it's so bad the only way I can tell what's going on is to compile it then *disassemble* it to see what the code actually is.) Contrast that with the dmd front end source where I've made a concerted effort (not 100% yet) to remove #ifdef's. And I didn't even touch on what would have to happen if versioning could slice anywhere - it would have to be done as a separate pre-pass. It couldn't be integrated in to the current one-pass parser, and would do a fine job of screwing up syntax highlighters and pretty-printers much like C's preprocessor can.

I have to debug somebody else code right now and in the name of portability is written in some kind of pseudo-language in C macros. The result is the as optlink, you have to pre-process the source to actually know what it's doing. But I think this is not because C is bad, this is because the people who wrote that code are animals, and I think the language shouldn't get in the way if you want to use it, we all are consenting adults =). I clearly agree with Walter that the right thing to do is separate OS-dependant functionality in different modules, and I think the D standard library should do that as an example of how things should be done in D (showing as a living example that that's the way to go). The same goes for version (!X) ..., I think it should be available, there are cases when the use is valid and you have to do artificial hacks like version (X) else .... It's like Java not having functions or global variable. You're just annoying people that know what they're doing to "protect" the idiots (which can go and use static methods and variables anyways; or version (X) else ...). I completely agree with Bill, that's patronizing... -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Un camión lleno de amigos, míos. Cada uno dando vueltas, en su cabeza. Mientras yo, sufro la picadura de mi propia abeja.
Jul 14 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Leandro Lucarella wrote:
 The same goes for version (!X) ..., I think it should be available, there
 are cases when the use is valid and you have to do artificial hacks like
 version (X) else .... It's like Java not having functions or global
 variable. You're just annoying people that know what they're doing to
 "protect" the idiots (which can go and use static methods and variables
 anyways; or version (X) else ...).

It's not about protecting idiots. It's about making the better way to do things the easier and more natural way, and making the worse more difficult. In C++, int a[5]; is the wrong way, and: std::vector<int>(5) a; is the right way. C++ makes the right way ugly and hard. I'd like to reverse that. All languages have some characteristics of "you shouldn't be allowed to do that", the problem is where the line is drawn. I have long, long experience with #ifdef's. I know how convenient it is to just plop those things in, like your first hit of heroin. I know how justifiable just that one little old #ifdef is. Then you add in another, and another, and another, and another, and eventually wonder how you wound up with such an impenetrable thicket of awfulness. My own code gets like that (despite my knowing better) and just about every long lived piece of C/C++/asm code I've run across. I do the same as you, running the preprocessor independently on C code to figure out what's happening. That, however, would be problematic with D as it doesn't have a preprocessor. You'd have to build a separate tool to do it.
Jul 14 2009
next sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
Walter Bright wrote:
 It's not about protecting idiots. It's about making the better way to do
 things the easier and more natural way, and making the worse more
 difficult.

Making the better way easy is a worthwhile goal. Making the worse way more difficult is not. A programming language should never set out to intentionally make things difficult for the programmer. A feature that is used in bad code now may eventually find use in new idioms that improve the overall quality of code in the future.
 I have long, long experience with #ifdef's. I know how convenient it is
 to just plop those things in, like your first hit of heroin. I know how
 justifiable just that one little old #ifdef is. Then you add in another,
 and another, and another, and another, and eventually wonder how you
 wound up with such an impenetrable thicket of awfulness. My own code
 gets like that (despite my knowing better) and just about every long
 lived piece of C/C++/asm code I've run across.

When I encounter bad code, I often try to refactor it. The overall trend is that the quality of my code improves over time. -- Rainer Deyke - rainerd eldwood.com
Jul 14 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Rainer Deyke wrote:
 Walter Bright wrote:
 It's not about protecting idiots. It's about making the better way to do
 things the easier and more natural way, and making the worse more
 difficult.

Making the better way easy is a worthwhile goal. Making the worse way more difficult is not. A programming language should never set out to intentionally make things difficult for the programmer.

Why do C and C++ (and D) make it difficult to do: char *p; p |= 1; ? There's no implementation difficulty in accepting such and generating correct code for it. It's purely a matter of making what is generally considered to be bad practice harder to do. I've never heard anyone argue that this was a bad decision.
 A feature that
 is used in bad code now may eventually find use in new idioms that
 improve the overall quality of code in the future.

#ifdef's have been around for 30 years now. I think that's long enough for them to prove themselves. May I remind everyone that D still does have conditional compilation. It isn't going away.
 I have long, long experience with #ifdef's. I know how convenient it is
 to just plop those things in, like your first hit of heroin. I know how
 justifiable just that one little old #ifdef is. Then you add in another,
 and another, and another, and another, and eventually wonder how you
 wound up with such an impenetrable thicket of awfulness. My own code
 gets like that (despite my knowing better) and just about every long
 lived piece of C/C++/asm code I've run across.

When I encounter bad code, I often try to refactor it. The overall trend is that the quality of my code improves over time.

We all make promises to ourselves to lose weight, too <g>.
Jul 14 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 On Tue, Jul 14, 2009 at 5:13 PM, Walter
 Bright<newshound1 digitalmars.com> wrote:
 Why do C and C++ (and D) make it difficult to do:

   char *p;
   p |= 1;

 ? There's no implementation difficulty in accepting such and generating
 correct code for it. It's purely a matter of making what is generally
 considered to be bad practice harder to do. I've never heard anyone argue
 that this was a bad decision.

I've never ever needed to do that, or been the slightest bit tempted to. The operation doesn't make sense. So I think the analogy is inappropos.

I beg to differ. My C++ compiler implementation of precompiled headers uses bit 0 to determine if a pointer needs to be adjusted based on where it is loaded into memory or not. Using the bottom two bits as flags (because the pointers were aligned) is not all that uncommon. I've seen it done by major companies on some major, very successful projects. Then there's the famous pointer "xor hack".
 In contrast, using the negation of a version makes plenty
 of sense.

As you wrote, we've hashed through that before. Over time, I've eliminated nearly all the negated conditionals from my code, and have been happier with the results. Half of them turned out to be bogus anyway, because I'd add a 3rd state and then latent undetected bugs would appear. Here's one of my faves: #ifndef _WIN32 ... do something for Linux ... #endif Does this happen in reality? I grepped #ifndef's from the Hans Boehm gc: version.h:#ifndef GC_NO_VERSION_VAR allchblk.c:#ifndef USE_MUNMAP alloc.c:#ifndef SMALL_CONFIG AmigaOS.c:#ifndef GC_AMIGA_FASTALLOC AmigaOS.c:#ifndef GC_AMIGA_ONLYFAST AmigaOS.c:#ifndef GC_AMIGA_ONLYFAST AmigaOS.c:#ifndef GC_AMIGA_ONLYFAST AmigaOS.c:#ifndef GC_AMIGA_ONLYFAST AmigaOS.c:#ifndef GC_AMIGA_ONLYFAST AmigaOS.c:#ifndef GC_AMIGA_FASTALLOC AmigaOS.c:#ifndef GC_AMIGA_ONLYFAST dbg_mlc.c:#ifndef SHORT_DBG_HDRS dbg_mlc.c:#ifndef SHORT_DBG_HDRS dbg_mlc.c:#ifndef SHORT_DBG_HDRS dbg_mlc.c:#ifndef SHORT_DBG_HDRS dyn_load.c:#ifndef _sigargs finalize.c:#ifndef NO_DEBUGGING finalize.c:#ifndef JAVA_FINALIZATION_NOT_NEEDED irix_threads.c:#ifndef LINT linux_threads.c:#ifndef __GNUC__ linux_threads.c:#ifndef SIG_THR_RESTART mach_dep.c:#ifndef USE_GENERIC_PUSH_REGS mach_dep.c:#ifndef SPARC mark.c:#ifndef THREADS mark.c:#ifndef UNALIGNED mark.c:#ifndef SMALL_CONFIG mark.c:#ifndef SMALL_CONFIG misc.c:#ifndef _WIN32_WCE misc.c:#ifndef PCR new_hblk.c:#ifndef SMALL_CONFIG os_dep.c:#ifndef HEAP_START os_dep.c:#ifndef THREADS solaris_pthreads.c:#ifndef LINT solaris_threads.c:#ifndef MMAP_STACKS solaris_threads.c:#ifndef LINT The #ifndef NO_DEBUGGING is awful. The #ifndef __GNUC__ means compile for every unknown compiler ever, except gcc. Can't possibly be right. Let's try grepping for !defined. We get: allchblk.c:# if !defined(NO_DEBUGGING) alloc.c:# if !defined(MACOS) && !defined(MSWINCE) alloc.c:# if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) alloc.c:# if !defined(NO_DEBUGGING) alloc.c:# if !defined(AMIGA) || !defined(GC_AMIGA_FASTALLOC) AmigaOS.c:#if !defined(GC_AMIGA_DEF) && !defined(GC_AMIGA_SB) && !defined(GC_AMIGA_DS) && !defined(GC_AMIGA_AM) checksums.c:# if !defined(MSWIN32) && !defined(MSWINCE) dyn_load.c:#if !defined(MACOS) && !defined(_WIN32_WCE) dyn_load.c: && !defined(GC_USE_LD_WRAP) dyn_load.c: && !defined(PCR) dyn_load.c:#if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && \ dyn_load.c: !defined(MSWIN32) && !defined(MSWINCE) && \ dyn_load.c: !defined(HPUX) && !(defined(LINUX) && defined(__ELF__)) && \ dyn_load.c: !defined(RS6000) && !defined(SCO_ELF) && \ dyn_load.c:#if defined(SUNOS5DL) && !defined(USE_PROC_FOR_LIBRARIES) dyn_load.c:#if defined(SUNOS4) && !defined(USE_PROC_FOR_LIBRARIES) dyn_load.c:# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS) dyn_load.c:# if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32 dyn_load.c:#if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX)) dyn_load.c:# if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION) finalize.c:# if !defined(THREADS) && !defined(DBG_HDRS_ALL) gc_dlopen.c:# if defined(dlopen) && !defined(GC_USE_LD_WRAP) linux_threads.c:# if defined(HPUX_THREADS) && !defined(USE_PTHREAD_SPECIFIC) \ linux_threads.c: && !defined(USE_HPUX_TLS) linux_threads.c:# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_HPUX_TLS) linux_threads.c:# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) linux_threads.c:# if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC) \ linux_threads.c: || !defined(__GNUC__) linux_threads.c:#if !defined(HPUX_THREADS) && !defined(GC_OSF1_THREADS) linux_threads.c:# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) linux_threads.c:# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_SPECIFIC) \ linux_threads.c: && !defined(USE_HPUX_TLS) && !defined(DBG_HDRS_ALL) linux_threads.c:# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) linux_threads.c:#if !defined(USE_SPIN_LOCK) || defined(PARALLEL_MARK) mach_dep.c:#if defined(__MWERKS__) && !defined(POWERPC) mach_dep.c:# if defined(I386) &&!defined(OS2) &&!defined(SVR4) \ mach_dep.c: && (defined(__MINGW32__) || !defined(MSWIN32)) \ mach_dep.c: && !defined(SCO) && !defined(SCO_ELF) \ mach_dep.c: && !defined(DOS4GW) mach_dep.c:# if defined(I386) && defined(MSWIN32) && !defined(__MINGW32__) \ mach_dep.c: && !defined(USE_GENERIC) mach_dep.c:# if !defined(POWERPC) && !defined(UTS4) mach_dep.c:# if !defined(PJ) && !(defined(MIPS) && defined(LINUX)) mach_dep.c:#if defined(ASM_CLEAR_CODE) && !defined(THREADS) MacOS.c:# if !defined(SHARED_LIBRARY_BUILD) MacOS.c:# if !defined(SILENT) && !defined(SHARED_LIBRARY_BUILD) mallocx.c:#if defined(THREADS) && !defined(SRC_M3) mark.c:# if !defined(IA64) && !defined(HP_PA) mark.c:#if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) mark.c:# if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) mark.c:# if !defined(SMALL_CONFIG) && !defined(UNALIGNED) && \ mark.c: !defined(USE_MARK_BYTES) mark_rts.c: # if !defined(MSWIN32) && !defined(MSWINCE) mark_rts.c:# if !defined(NO_DEBUGGING) mark_rts.c:#if !defined(MSWIN32) && !defined(MSWINCE) mark_rts.c:# if !defined(MSWIN32) && !defined(MSWINCE) mark_rts.c:# if !defined(MSWIN32) && !defined(MSWINCE) mark_rts.c:# if !defined(MSWIN32) && !defined(MSWINCE) mark_rts.c: || defined(PCR)) && !defined(SRC_M3) mark_rts.c:# if !defined(USE_GENERIC_PUSH_REGS) misc.c: || defined(LINUX_THREADS) && !defined(USE_SPIN_LOCK) misc.c:# if !defined(THREADS) && defined(GC_ASSERTIONS) misc.c:# if !defined(THREADS) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ misc.c:# if !defined(STACK_GROWS_UP) && !defined(STACK_GROWS_DOWN) misc.c:# if !defined(_AUX_SOURCE) || defined(__GNUC__) misc.c:# if !defined(SMALL_CONFIG) misc.c:#if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) && !defined(MSWINCE) misc.c:# if !defined(AMIGA) misc.c:#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(OS2) && !defined(MACOS) misc.c:#if defined(LINUX) && !defined(SMALL_CONFIG) misc.c:#if !defined(NO_DEBUGGING) os_dep.c:# if defined(LINUX) && !defined(POWERPC) os_dep.c:# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \ os_dep.c: && !defined(MSWINCE) os_dep.c:# if !defined(MSWIN32) && !defined(SUNOS4) os_dep.c:# if !defined(THREADS) && !defined(STACKBOTTOM) && defined(HEURISTIC2) os_dep.c:# if (defined(SUNOS4) && defined(DYNAMIC_LOADING)) && !defined(PCR) os_dep.c:# if (defined(SVR4) || defined(AUX) || defined(DGUX)) && !defined(PCR) os_dep.c:#if !defined(NO_EXECUTE_PERMISSION) os_dep.c:# if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */ os_dep.c:# if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \ os_dep.c: && !defined(MSWINCE) \ os_dep.c: && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) os_dep.c:# if defined(sigmask) && !defined(UTS4) os_dep.c:#if defined(PRINTSTATS) && !defined(THREADS) os_dep.c:#if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \ os_dep.c: && !defined(MSWINCE) && !defined(OS2) os_dep.c: || (defined(LINUX) && defined(SPARC))) && !defined(PCR) os_dep.c:# if !defined(PCR) && !defined(SRC_M3) && !defined(NEXT) && !defined(MACOS) \ os_dep.c: && !defined(MACOSX) os_dep.c:# if !defined(PCR) && (defined(NEXT) || defined(MACOSX)) os_dep.c:# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \ os_dep.c: && !defined(MSWIN32) && !defined(MSWINCE) \ os_dep.c: && !defined(MACOS) && !defined(DOS4GW) os_dep.c:# if !defined(LINUX) os_dep.c:#if !defined(MSWIN32) && !defined(MSWINCE) os_dep.c:# if !defined(MSWIN32) && !defined(MSWINCE) os_dep.c:# if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(IA64) os_dep.c:# if !defined(MSWIN32) && !defined(MSWINCE) os_dep.c:#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(LINUX_THREADS) \ os_dep.c: && !defined(GC_USE_LD_WRAP) os_dep.c:# if defined(__STDC__) && !defined(SUNOS4) os_dep.c: (!defined(SMALL_CONFIG) || defined(USE_PROC_FOR_LIBRARIES)) os_dep.c:#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) ptr_chck.c: && !defined(SRC_M3) reclaim.c:#if !defined(SMALL_CONFIG) && defined(USE_MARK_BYTES) reclaim.c:#if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) reclaim.c:#if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) reclaim.c:#if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) reclaim.c:# if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) reclaim.c:# if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) reclaim.c:#if !defined(NO_DEBUGGING) stlport is not quite as bad: src\c_locale_win32\c_locale_win32.c:#if !defined (LC_MAX) stlport\stl\_algo.c:# if !defined (_STLP_INTERNAL_ALGO_H) stlport\stl\_algobase.c:# if !defined (_STLP_INTERNAL_ALGOBASE_H) stlport\stl\_limits.c:# if !defined (_STLP_LIMITS_C) stlport\stl\_list.c:#if !defined (__WATCOMC__) stlport\stl\_num_get.c:# if defined(_STLP_LONG_LONG)&&!defined(__MRC__) //*ty 12/07/2001 - MrCpp can not cast from long long to void* stlport\stl\_num_put.c:# if defined(_STLP_LONG_LONG) && !defined(__MRC__) //*ty 11/24/2001 - MrCpp can not cast from void* to long long stlport\stl\_ostream.c:#if !defined (_STLP_INTERNAL_NUM_PUT_H) stlport\stl\_rope.c:#if !defined (_STLP_USE_NO_IOSTREAMS) stlport\stl\_rope.c:#if defined(__MRC__)||(defined(__SC__) && !defined(__DMC__)) //*TY 05/23/2000 - added support for mpw compiler's trigger function approach to generate vtable stlport\stl\_rope.c:# if defined(__MRC__)||(defined(__SC__) && !defined(__DMC__)) //*TY 05/23/2000 - added support for mpw compiler's trigger function approach to generate vtable stlport\stl\_rope.c:# if defined(__MRC__)||(defined(__SC__) && !defined(__DMC__)) //*TY 05/23/2000 - added support for mpw compiler's trigger function approach to generate vtable stlport\stl\_rope.c:# if !defined (_STLP_NO_METHOD_SPECIALIZATION) stlport\stl\_rope.c:#endif /* if !defined (_STLP_USE_NO_IOSTREAMS) */ stlport\stl\_rope.c:#if !defined (_STLP_USE_NO_IOSTREAMS) stlport\stl\_rope.c:# if !defined(__GC) && defined(_STLP_USE_EXCEPTIONS) stlport\stl\_streambuf.c:# if !defined (_STLP_NO_WCHAR_T) stlport\stl\_string.c:# if defined (_STLP_USE_OWN_NAMESPACE) || !defined (_STLP_USE_NATIVE_STRING) stlport\stl\_string.c:# if !defined (_STLP_LINK_TIME_INSTANTIATION) stlport\stl\_string_fwd.c:#if !defined ( _STLP_STRING_FWD_C) && ! defined (_STLP_OWN_IOSTREAMS) stlport\stl\_threads.c:# if !defined ( _STLP_ATOMIC_EXCHANGE ) && (defined (_STLP_PTHREADS) || defined (_STLP_UITHREADS) || defined (_STLP_OS2THREADS)) stlport\stl\_vector.c:# if !defined (_STLP_INTERNAL_VECTOR_H) stlport\stl\debug\_debug.c:# if !defined( _STLP_DEBUG_MESSAGE ) src\c_locale_win32\c_locale_win32.c:#ifndef _LEADBYTE See all those !(NO_FEATURE) constructs? Not no-how, not no-way! And people say my use of goto is bad and should be taken away from me :-) I tried it on the Microsoft Windows api headers. Many pages of very dubious use follow, no need to post it all here. This stuff is marquee showcase code by professional well-paid corporate developers, not idiots or newbies. I slip into writing such crud, too.
 As does versioning out an entry in an enum that doesn't
 apply for some reason.

Can we discuss a real example of this? I think perhaps we can find a way to accommodate the desired result (if not the method) in a clean manner.
Jul 14 2009
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:
 Using the bottom two bits as flags (because the pointers were aligned) 
 is not all that uncommon. I've seen it done by major companies on some 
 major, very successful projects.

That's done often enough, one or two bits in tagged pointers are useful to implement various data structures (and they are used by garbage collectors too). I even used tagged pointers once in D, using memory allocated from the C heap. Bye, bearophile
Jul 14 2009
prev sibling parent reply grauzone <none example.net> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 On Tue, Jul 14, 2009 at 5:13 PM, Walter
 Bright<newshound1 digitalmars.com> wrote:
 Why do C and C++ (and D) make it difficult to do:

   char *p;
   p |= 1;

 ? There's no implementation difficulty in accepting such and generating
 correct code for it. It's purely a matter of making what is generally
 considered to be bad practice harder to do. I've never heard anyone 
 argue
 that this was a bad decision.

I've never ever needed to do that, or been the slightest bit tempted to. The operation doesn't make sense. So I think the analogy is inappropos.


Often I wanted to write p &= ~3 in low level code. And that does make sense, because it aligns the pointer.
 The #ifndef NO_DEBUGGING is awful. The #ifndef __GNUC__ means compile 
 for every unknown compiler ever, except gcc. Can't possibly be right.

The #ifndef NO_DEBUGGING causes the code to be compiled in debugging mode, if it isn't explicitly deactivated. This is a good thing. I think dmd should compile in debug mode too, and force the user to pass -nodebug to disable it. The #ifndef _GNUC_ is probably for using gcc compiler/libc extensions, which makes sense.
Jul 15 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
grauzone wrote:
 The #ifndef NO_DEBUGGING causes the code to be compiled in debugging 
 mode, if it isn't explicitly deactivated.

"You, sir, are employing a double negative." -- Mr. Spock It's not that it's impossible to figure out, it's that it's execrable style. In my experience, such constructs correlate strongly with incoherent and buggy code in the sections it appears in, including when I've used it. The interesting question is is the construct itself the cause or the effect of incoherence and bugginess? I'm asking you to give it a chance. Grep your own code for such. See if it could be redone without negation, and see if you like that better. Even so, you *can* use negation in D version statements: version (NO_DEBUGGING) {} else { ... debugging is on ... } they're just a few more characters than laying down a single ! or n. Hopefully, they're enough extra effort that one will minimize the use of them.
Jul 15 2009
parent reply grauzone <none example.net> writes:
 The interesting question is is the construct itself the cause or the 
 effect of incoherence and bugginess?

Maybe. But the real problem is, that you can't force the user to specify either a "yes" or "no" for a specific version/define identifier. He still could just forget about it. Of course, you could always write something like: version (DEBUGGING) { } else version (NODEBUGGING) { } else { static assert(false, "you forgot something!"); } (Or something similar.) But that's a bit annoying.
 Even so, you *can* use negation in D version statements:
 
     version (NO_DEBUGGING) {} else
     {
     ... debugging is on ...
     }

Only marginally better than a #ifndef NO_DEBUGGING.
Jul 15 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
The idea is to make writing garbage more work than doing it right. But 
not make it impossible.
Jul 15 2009
prev sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
grauzone wrote:

 Walter Bright wrote:
 Bill Baxter wrote:
 On Tue, Jul 14, 2009 at 5:13 PM, Walter
 Bright<newshound1 digitalmars.com> wrote:
 Why do C and C++ (and D) make it difficult to do:

   char *p;
   p |= 1;

 ? There's no implementation difficulty in accepting such and generating
 correct code for it. It's purely a matter of making what is generally
 considered to be bad practice harder to do. I've never heard anyone
 argue
 that this was a bad decision.

I've never ever needed to do that, or been the slightest bit tempted to. The operation doesn't make sense. So I think the analogy is inappropos.


Often I wanted to write p &= ~3 in low level code. And that does make sense, because it aligns the pointer.
 The #ifndef NO_DEBUGGING is awful. The #ifndef __GNUC__ means compile
 for every unknown compiler ever, except gcc. Can't possibly be right.

The #ifndef NO_DEBUGGING causes the code to be compiled in debugging mode, if it isn't explicitly deactivated. This is a good thing. I think dmd should compile in debug mode too, and force the user to pass -nodebug to disable it.

So that reads as: if not not actived then if not defined no debugging then do stuff nice!
Jul 15 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Lutger wrote:
 if not not actived then if not defined no debugging then do stuff

Ain't nobody doing nothing around here! Not no how, not no way! No sirree!
Jul 15 2009
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 You do realize you're being patronizing, right?  "I have so much
 experience with these things, and I know the right way to write code,
 and you don't, so I'm not going to give you this thing you request
 because it's not good for you".

Sure. There's some of that in every language. They all have some feature or other rejected because the designers, be they individuals or committees, considered such features as encouraging bad practice. Even C++ has this. Why do you think doing | operations on pointers is illegal? It's certainly not a limitation on compiler technology, and there certainly are legitimate uses for it. But the designers felt that was an execrable practice that should be banned. The resumption exception handling model for C++ was rejected because it was felt such encouraged bad practices.
 Also note that despite D's limitations supposedly making things
 better, you just got through describing how parts of Phobos turned
 into a mess anyway.

It's not as good as I would like it to be, but it's also not near as bad with conditional compilation as other libraries I've dealt with.
 So not only do the little missing capabilities annoy people who would
 use them judiciously, they also apparently don't have the desired
 outcome of eliminating poor use of conditional compilation.  Sounds
 like something you would find in a patronizing nanny-language to me.
 Which is odd, because D isn't like that, overall.

Rather than interpret it as patronizing, I ask you to try it my way for a while. Give it a fair shake. Note that D *still has* conditional compilation, and there are no plans to remove it.
Jul 14 2009
prev sibling parent Leandro Lucarella <llucax gmail.com> writes:
Walter Bright, el 14 de julio a las 14:52 me escribiste:
 Leandro Lucarella wrote:
The same goes for version (!X) ..., I think it should be available, there
are cases when the use is valid and you have to do artificial hacks like
version (X) else .... It's like Java not having functions or global
variable. You're just annoying people that know what they're doing to
"protect" the idiots (which can go and use static methods and variables
anyways; or version (X) else ...).

It's not about protecting idiots. It's about making the better way to do things the easier and more natural way, and making the worse more difficult. In C++, int a[5]; is the wrong way, and: std::vector<int>(5) a; is the right way. C++ makes the right way ugly and hard. I'd like to reverse that. All languages have some characteristics of "you shouldn't be allowed to do that", the problem is where the line is drawn. I have long, long experience with #ifdef's. I know how convenient it is to just plop those things in, like your first hit of heroin. I know how justifiable just that one little old #ifdef is. Then you add in another, and another, and another, and another, and eventually wonder how you wound up with such an impenetrable thicket of awfulness. My own code gets like that (despite my knowing better) and just about every long lived piece of C/C++/asm code I've run across.

I think you can always use it right when you have only one "#ifdef", and when you see that it's getting messy, you can refactor your code to go the way of "module personalities". If you can do that, the language can scale up *and* down. Otherwise the language is only suitable for big systems. Again, compare it to Java: for big systems is usually a good thing to use an architecture with several layers and OO with very "hard" interfaces. In Java you can only use that, you can't hack a quick script because it doesn't scale down. I think D should handle both worlds gracefully.
 I do the same as you, running the preprocessor independently on C code
 to figure out what's happening. That, however, would be problematic with
 D as it doesn't have a preprocessor. You'd have to build a separate tool
 to do it.

Again, people can make bad code in D anyways, you just make it a little harder, at the expense of making a little harder to use version more flexibly when it's right to do so (as Java make it harder to make a free function or a global variable). -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Wenn ist das nunstück git und slotermeyer? Ja! Beiherhund das oder die Flipperwaldt gersput! -- Monty Python (no leer si sabés alemán)
Jul 14 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Mon, Jul 13, 2009 at 10:05 PM, Walter
Bright<newshound1 digitalmars.com> wrote:
 Julian Salazar wrote:
 It's been rehashed here several times (not to rag on you, just to point out
 that it isn't something that's been overlooked). To sum up, I've worked a
 lot with both styles - #ifdef, and separating dependencies into independent
 modules. The latter works a lot better. I know it's hard to believe if
 you're used to the #ifdef style. I've been doing some work to remove
 #ifdef's from the dmd compiler source, and the results so far have been very
 satisfactory.

But from where I sit it looked like Walter didn't really convince anyone. To me this seems like a point where D is overly patronizing, to use the phrase from a recent post. --bb
Jul 14 2009
prev sibling next sibling parent Tomas Lindquist Olsen <tomas.l.olsen gmail.com> writes:
On Mon, Jul 13, 2009 at 8:24 PM, Walter
Bright<newshound1 digitalmars.com> wrote:
 Julian Salazar wrote:
 Hi, I'm new here to the community but I've been using D for a while now,
 and I have to say that it's a great programming language. I'd like to ge=


 involved in this community and help shape this language.

Welcome!
 I'm just wondering about a minor issue: why are conditional blocks inval=


 within expressions such as enum and asm? I mean, in trivial cases it's f=


 but in instances where code duplication is a big maintainability nightma=


 making conditional compilation more flexible would have benefits for
 developers.

The request to do it for enums has been thrashed about before. Essentiall=

 version works at the declaration and statement level, not at the expressi=

 level or in between tokens, etc. The reason for this is to encourage a mo=

 modular approach to versioning than the typical C method of doing it at t=

 lowest level.


 Something like (I know it's a trivial example, but you get the point):

 asm {
 =C2=A0 version(x86) mov EAX, 1;
 =C2=A0 else version(x86_64) mov EAX, 2;
 }

 would trigger an error. Also, though I know enum qualifies as a
 constant/datatype cross, structs and classes are perfectly fine with
 conditional compilation. Couldn't the lexical stuff be changed to suppor=


 for enum and asm as well?

Let me illustrate by a current example. The linker (optlink) is written 1=

 in assembler. This makes it rather intractable. It's also loaded up with
 line-by-line nested conditional assembly (and a lot of macros). It's so h=

 to see what is *actually* being compiled that I'll assemble it, run OBJ2A=

 on the output, and work off of the disassembled code.

 So, in essence, the idea is to push conditional compilation to higher
 levels, not lower levels. Ideally, versioning would be done by abstractin=

 all the version differences into an interface implemented by different
 modules.


 Also, I noticed that there is no formal specification page for x86-64
 inline assembly. You define a predefined version identifier such as
 D_InlineAsm_X86_64, but you don't define registers and instructions
 pertaining to it. In GDC for example, using the RAX register in the D in=


 ASM syntax is invalid. Not sure what the case is in LDC (they probably d=


 implement it for x86-64), and I know DMD does not have a 64-bit version,=


 the spec should at least have a definition for compilers that do impleme=


 64-bit support.

The first approximation to the definition is to use the Intel asm syntax =

 outlined in their processor data sheets. I haven't written a spec more
 detailed than that because it's a lot of work and I'm lazy, and such work=

 not terribly exciting. But if you'd like to help with that, I'd welcome i=


LDC implements x86-64 inline asm just like x86-32 inline asm, that is the syntax is the same, and the only real difference is the new 64bit registers and opcodes. Of course a spec would be nice, but they're really so similar that it might well be a single page if you ask me.
Jul 14 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Tue, Jul 14, 2009 at 10:01 AM, Walter
Bright<newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 But from where I sit it looked like Walter didn't really convince
 anyone. =A0To me this seems like a point where D is overly patronizing,
 to use the phrase from a recent post.

You could argue that, but it also took a long time to convince many about the merit of const and immutable. I understand that C style versioning is=

 seductive, it's very hard to see what's wrong with it.

 (For another reason against such, I could send you some of the source to
 optlink. It's chock full of line by line versioning, nested versioning, a
 couple dozen version arguments, it's so bad the only way I can tell what'=

 going on is to compile it then *disassemble* it to see what the code
 actually is.)

 Contrast that with the dmd front end source where I've made a concerted
 effort (not 100% yet) to remove #ifdef's.

 And I didn't even touch on what would have to happen if versioning could
 slice anywhere - it would have to be done as a separate pre-pass. It
 couldn't be integrated in to the current one-pass parser, and would do a
 fine job of screwing up syntax highlighters and pretty-printers much like
 C's preprocessor can.

I wasn't very clear. I do think you make a convincing argument that in general lots of micro ifdefs everywhere is not the right approach. But I remain unconvinced that potential for abuse is a good reason to disallow finer scale version() statements. It smacks of the same patronizing one-size-fits-all attitude of java, where everything has to be a class because the designers decided that OO=3D=3Dbetter design. It just doesn't seem to fit well with the rest of the D attitude of not getting in the developer's way. --bb
Jul 14 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Tue, Jul 14, 2009 at 12:04 PM, Walter
Bright<newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 I do think you make a convincing argument that in general lots of
 micro ifdefs everywhere is not the right approach.
 But I remain unconvinced that potential for abuse is a good reason to
 disallow finer scale version() statements.

It's where the line between micro and fine is that you disagree with, not the principle?

Yeh. Like in the case just mentioned -- version() around items in an enum sounds more reasonable to me than having to define the entire enum twice. --bb
Jul 14 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Tue, Jul 14, 2009 at 2:52 PM, Walter
Bright<newshound1 digitalmars.com> wrote:
 Leandro Lucarella wrote:
 The same goes for version (!X) ..., I think it should be available, ther=


 are cases when the use is valid and you have to do artificial hacks like
 version (X) else .... It's like Java not having functions or global
 variable. You're just annoying people that know what they're doing to
 "protect" the idiots (which can go and use static methods and variables
 anyways; or version (X) else ...).

It's not about protecting idiots. It's about making the better way to do things the easier and more natural way, and making the worse more difficu=

 In C++,

 =A0 int a[5];

 is the wrong way, and:

 =A0 std::vector<int>(5) a;

 is the right way. C++ makes the right way ugly and hard. I'd like to reve=

 that.

 All languages have some characteristics of "you shouldn't be allowed to d=

 that", the problem is where the line is drawn.

 I have long, long experience with #ifdef's. I know how convenient it is t=

 just plop those things in, like your first hit of heroin. I know how
 justifiable just that one little old #ifdef is. Then you add in another, =

 another, and another, and another, and eventually wonder how you wound up
 with such an impenetrable thicket of awfulness. My own code gets like tha=

 (despite my knowing better) and just about every long lived piece of
 C/C++/asm code I've run across.

You do realize you're being patronizing, right? "I have so much experience with these things, and I know the right way to write code, and you don't, so I'm not going to give you this thing you request because it's not good for you". Also note that despite D's limitations supposedly making things better, you just got through describing how parts of Phobos turned into a mess anyway. So not only do the little missing capabilities annoy people who would use them judiciously, they also apparently don't have the desired outcome of eliminating poor use of conditional compilation. Sounds like something you would find in a patronizing nanny-language to me. Which is odd, because D isn't like that, overall. --bb
Jul 14 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Tue, Jul 14, 2009 at 5:02 PM, Walter
Bright<newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 You do realize you're being patronizing, right? =A0"I have so much
 experience with these things, and I know the right way to write code,
 and you don't, so I'm not going to give you this thing you request
 because it's not good for you".

Sure. There's some of that in every language. They all have some feature =

 other rejected because the designers, be they individuals or committees,
 considered such features as encouraging bad practice.

 Even C++ has this. Why do you think doing | operations on pointers is
 illegal? It's certainly not a limitation on compiler technology, and ther=

 certainly are legitimate uses for it. But the designers felt that was an
 execrable practice that should be banned.

 The resumption exception handling model for C++ was rejected because it w=

 felt such encouraged bad practices.

 Also note that despite D's limitations supposedly making things
 better, you just got through describing how parts of Phobos turned
 into a mess anyway.

It's not as good as I would like it to be, but it's also not near as bad with conditional compilation as other libraries I've dealt with.
 So not only do the little missing capabilities annoy people who would
 use them judiciously, they also apparently don't have the desired
 outcome of eliminating poor use of conditional compilation. =A0Sounds
 like something you would find in a patronizing nanny-language to me.
 Which is odd, because D isn't like that, overall.

Rather than interpret it as patronizing, I ask you to try it my way for a while. Give it a fair shake.

I think your way is fine. But I also think that there are lots of versions that are binary in nature, like "version(HaveSomeAPI)", where the natural thing to do to supplant the functionality is version(!HaveSomeAPI) { // replacement implementation of SomeAPI goes here } and not version(HaveSomeAPI) {} else { // replacement implementation of SomeAPI goes here } and also not version(HaveSomeAPI) {} else { version =3D HaveNotSomeAPI; } version(HaveNotSomeAPI) { // replacement implementation of SomeAPI goes here } I'm not arguing with you about the big picture of how to organize non-trivial conditional versioning. I'm more arguing that removing ! from version does little to encourage good coding practices and plenty to hinder reasonable ones. Anyway, I don't expect you to change your mind since we've pretty much been over all this before, so I'll leave it be at this. --bb
Jul 14 2009
prev sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Tue, Jul 14, 2009 at 5:13 PM, Walter
Bright<newshound1 digitalmars.com> wrote:
 Rainer Deyke wrote:
 Walter Bright wrote:
 It's not about protecting idiots. It's about making the better way to d=



 things the easier and more natural way, and making the worse more
 difficult.

Making the better way easy is a worthwhile goal. =A0Making the worse way more difficult is not. =A0A programming language should never set out to intentionally make things difficult for the programmer.

Why do C and C++ (and D) make it difficult to do: =A0 char *p; =A0 p |=3D 1; ? There's no implementation difficulty in accepting such and generating correct code for it. It's purely a matter of making what is generally considered to be bad practice harder to do. I've never heard anyone argue that this was a bad decision.

I've never ever needed to do that, or been the slightest bit tempted to. The operation doesn't make sense. So I think the analogy is inappropos. In contrast, using the negation of a version makes plenty of sense. As does versioning out an entry in an enum that doesn't apply for some reason. ---bb
Jul 14 2009
prev sibling parent reply Don <nospam nospam.com> writes:
Julian Salazar wrote:
 Hi, I'm new here to the community but I've been using D for a while now, 
 and I have to say that it's a great programming language. I'd like to 
 get involved in this community and help shape this language.

Welcome!
 I'm just wondering about a minor issue: why are conditional blocks 
 invalid within expressions such as enum and asm? I mean, in trivial 
 cases it's fine, but in instances where code duplication is a big 
 maintainability nightmare, making conditional compilation more flexible 
 would have benefits for developers.
 
 Something like (I know it's a trivial example, but you get the point):
 
 asm {
    version(x86) mov EAX, 1;
    else version(x86_64) mov EAX, 2;
 }
 
 would trigger an error. 

A much more convincing example is with position-independent code for Linux shared libraries. In this case you may have a long function, with only a single instruction right in the middle which needs to be changed. This example is taken from my bigint library. In the middle of a 50-line function is this single PIC-dependent instruction: asm { : : adc ECX, EDX; version (D_PIC) {} else { // the next trick can't be done in PIC mode. mov storagenop, EDX; // make #uops in loop a multiple of 3 } mul int ptr [ESP + 8]; : } But I'd class this as a minor annoyance rather than a significant problem.
Jul 14 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don wrote:
 In this case you may have a long function, with only a single 
 instruction right in the middle which needs to be changed.

void foo() { asm { mov EAX,EAX; ... lots more instructions ... } version (bar) asm { mov EAX,EAX; } asm { ... even more instructions ... mov EAX,EAX; } }
Jul 14 2009
next sibling parent reply Trass3r <mrmocool gmx.de> writes:
Walter Bright schrieb:
 Don wrote:
 In this case you may have a long function, with only a single 
 instruction right in the middle which needs to be changed.

void foo() { asm { mov EAX,EAX; ... lots more instructions ... } version (bar) asm { mov EAX,EAX; } asm { ... even more instructions ... mov EAX,EAX; } }

Man, it's so obvious, yet I wouldn't have hit on that ;)
Jul 14 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Trass3r wrote:
 Man, it's so obvious, yet I wouldn't have hit on that ;)

Most obvious things are obvious only in hindsight <g>.
Jul 14 2009
prev sibling next sibling parent Don <nospam nospam.com> writes:
Walter Bright wrote:
 Don wrote:
 In this case you may have a long function, with only a single 
 instruction right in the middle which needs to be changed.

void foo() { asm { mov EAX,EAX; ... lots more instructions ... } version (bar) asm { mov EAX,EAX; } asm { ... even more instructions ... mov EAX,EAX; } }

you want to _remove_ code for version(bar). asm { ... } version(D_PIC) {} else asm { ... } asm { ... }
Jul 15 2009
prev sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Tomas Lindquist Olsen wrote:
 On Wed, Jul 15, 2009 at 2:18 AM, Walter
 Bright<newshound1 digitalmars.com> wrote:
 Don wrote:
 In this case you may have a long function, with only a single instruction
 right in the middle which needs to be changed.

{ asm { mov EAX,EAX; ... lots more instructions ... } version (bar) asm { mov EAX,EAX; } asm { ... even more instructions ... mov EAX,EAX; } }

Since when does D guarantee that no code is inserted before/after asm blocks?

I think it was around the time that the mail service guaranteed that they wouldn't leave a dead cat in your mailbox on every alternate Wednesday.
Jul 15 2009
next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Daniel Keep wrote:
 I think it was around the time that the mail service guaranteed that
 they wouldn't leave a dead cat in your mailbox on every alternate Wednesday.

So that's why my mailbox stinks!
Jul 15 2009
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Tomas Lindquist Olsen wrote:
 And besides this is not in the spec, it might be implied, somehow, but
 it's not in the spec.

It never occurred to me that this might be necessary, but it obviously is.
Jul 15 2009
prev sibling next sibling parent Tomas Lindquist Olsen <tomas.l.olsen gmail.com> writes:
On Wed, Jul 15, 2009 at 2:18 AM, Walter
Bright<newshound1 digitalmars.com> wrote:
 Don wrote:
 In this case you may have a long function, with only a single instructio=


 right in the middle which needs to be changed.

void foo() { =C2=A0 =C2=A0asm =C2=A0 =C2=A0{ =C2=A0 =C2=A0 =C2=A0 =C2=A0mov EAX,EAX; =C2=A0 =C2=A0 =C2=A0 =C2=A0... lots more instructions ... =C2=A0 =C2=A0} =C2=A0 =C2=A0version (bar) asm =C2=A0 =C2=A0{ =C2=A0 =C2=A0 =C2=A0 =C2=A0mov EAX,EAX; =C2=A0 =C2=A0} =C2=A0 =C2=A0asm =C2=A0 =C2=A0{ =C2=A0 =C2=A0 =C2=A0 =C2=A0... even more instructions ... =C2=A0 =C2=A0 =C2=A0 =C2=A0mov EAX,EAX; =C2=A0 =C2=A0} }

Since when does D guarantee that no code is inserted before/after asm block= s?
Jul 15 2009
prev sibling parent Tomas Lindquist Olsen <tomas.l.olsen gmail.com> writes:
On Wed, Jul 15, 2009 at 12:24 PM, Daniel
Keep<daniel.keep.lists gmail.com> wrote:
 Tomas Lindquist Olsen wrote:
 On Wed, Jul 15, 2009 at 2:18 AM, Walter
 Bright<newshound1 digitalmars.com> wrote:
 Don wrote:
 In this case you may have a long function, with only a single instruct=




 right in the middle which needs to be changed.

{ =C2=A0 =C2=A0asm =C2=A0 =C2=A0{ =C2=A0 =C2=A0 =C2=A0 =C2=A0mov EAX,EAX; =C2=A0 =C2=A0 =C2=A0 =C2=A0... lots more instructions ... =C2=A0 =C2=A0} =C2=A0 =C2=A0version (bar) asm =C2=A0 =C2=A0{ =C2=A0 =C2=A0 =C2=A0 =C2=A0mov EAX,EAX; =C2=A0 =C2=A0} =C2=A0 =C2=A0asm =C2=A0 =C2=A0{ =C2=A0 =C2=A0 =C2=A0 =C2=A0... even more instructions ... =C2=A0 =C2=A0 =C2=A0 =C2=A0mov EAX,EAX; =C2=A0 =C2=A0} }

Since when does D guarantee that no code is inserted before/after asm bl=


 I think it was around the time that the mail service guaranteed that
 they wouldn't leave a dead cat in your mailbox on every alternate Wednesd=


Funny. But seriously, it actually takes some effort to make sure all successive asm statements are merged into a single asm block when generating code for LLVM. The DMD frontend "unpacks" asm blocks, so each line is a seperate AsmStatement, and with version blocks, you have to look into blocks as well. LLVM will restore registers etc. when an asm block is done! And besides this is not in the spec, it might be implied, somehow, but it's not in the spec.
Jul 15 2009