July 15, 2009
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 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.

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
July 15, 2009
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.
July 15, 2009
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
July 15, 2009
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 ;)
July 15, 2009
Trass3r wrote:
> Man, it's so obvious, yet I wouldn't have hit on that ;)

Most obvious things are obvious only in hindsight <g>.
July 15, 2009
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)
July 15, 2009
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;
>     }
> }
Yes, of course, that is what I do. It's ugly, though, especially since you want to _remove_ code for version(bar).
asm {
...
}
version(D_PIC) {} else asm {
...
}
asm {
...
}
July 15, 2009
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.
July 15, 2009
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.
July 15, 2009
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.
>
> void foo()
> {
>    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?