version and extern problems

Mike Parker aldacron71 at yahoo.com
Sat Jul 7 22:07:38 PDT 2007


Walter Bright wrote:
> Bug 1311 http://d.puremagic.com/issues/show_bug.cgi?id=1311
> is about using version declarations to control part of a following 
> declaration (or series of declarations):
> 
> -------------------------
> version(Windows)
>     extern(Windows):
> else
>     extern(C):
> 
> typedef void function() foo;
> --------------------------
> 
> This does not work now, and it was a bug that it ever did appear to 
> work. The issue is that version declarations can only affect entire 
> declarations, not parts of them. An extern statement with a : is 
> implicitly the same as:
> 
> ----------------
> extern(Windows):
> int a;
> int b;
> -----is same as---
> extern(Windows)
> {
>    int a;
>    int b;
> }
> -----------------

Right. But since version doesn't create a new scope, what's the problem?

> 
> That cannot be split up with a version declaration; it would be like 
> trying to make:
> 
>     version (Windows)
>         int
>     else
>         long
>     x;
> 
> work. The old behavior of dmd contained a serious (unreported) bug where 
> such constructs would cause forward references to behave unpredictably.

Then I misunderstood the usage of version. I was under the impression 
that it's a form of conditional compilation, i.e. on Windows the final 
result would be

int x;

Is that not possible?

> 
> So, the question is how to get the same effect? The alternatives are:
> 
> 1.
> version(Windows)
> {
>     extern(Windows):
>        typedef void function() foo;
> }
> else
> {
>     extern(C):
>        typedef void function() foo;
> }
> 
> Yes, that means doing a cut & paste on the code in the braces. Not 
> thrilling, but it works.

I implemented it as I did to avoid this. Using multiple declarations is 
error prone and a maintenance nightmare, particularly for the number of 
declarations I'm dealing with.

> 
> 2. Stop using extern(Windows). The Windows calling convention is only 
> necessary when a) calling Win32 API functions (which don't exist on 
> Linux anyway) and b) calling someone else's C code that pointlessly and 
> spuriously uses the Windows calling convention, and cannot be fixed.
> There is no reason in new C/C++ code to ever use the Windows calling 
> convention.

Unfortunately, I have no choice. This is not new D code, but bindings to 
existing cross-platform C libraries (namely OpenGL and DevIL). On 
Windows, these libraries all use the stdcall calling convention. 
Declaring them as extern(C) causes crashes. On other platforms, the 
libraries use the C calling convention, so they can't be declared 
extern(Windows) there.

Every D OpenGL binding I have seen uses this technique. That's how I 
first learned it. For example, all of Kenta Cho's D games 
(http://www.asahi-net.or.jp/~cs8k-cyu/index_e.html) use an OpenGL module 
with this at the top:

version (Win32) {
	private import std.c.windows.windows;
	extern(Windows):
}
else {
	extern(C):
}

followed by the function declarations. Most of his code already won't 
compile on newer versions without modification anyway. But still, now 
all of the D OpenGL bindings are broken.

> 
> 3. Create two source files, one for the extern(Windows) and the other 
> for the extern(C). Have your makefile automatically copy one to the 
> other, using sed to edit that one line. Import one under Windows, the 
> other under Linux.

Yuck! :)

> 
> 4. Wait for the future 2.0 macro feature, which should be able to deal 
> with this nicely:
> 
> macro Foo()
> {
>     typedef void function() foo;
> }
> version(Windows)
> {   extern(Windows): Foo(); }
> else
> {    extern(C): Foo(); }

That will be nice when it gets here, but there are already a lot of 
people using this library who aren't going to be willing to wait.

> 
> 5. Do the same as (4) using string mixins:
> 
> const string Foo =
> "
>     typedef void function() foo;
> ";
> version(Windows)
> {   extern(Windows): mixin(Foo); }
> else
> {    extern(C): mixin(Foo); }
> 
> 
> 6. Use template mixins:
> 
> template Foo()
> {
>     typedef void function() foo;
> }
> version(Windows)
> {   extern(Windows): mixin Foo; }
> else
> {    extern(C): mixin Foo; }

It looks like one of these is going to be the best alternative. More 
typing, but at least it keeps the declarations in one place.



More information about the Digitalmars-d mailing list