July 08, 2007
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;
}
-----------------

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.

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.

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.

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.

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(); }

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; }
July 08, 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.
July 08, 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;
> }
> -----------------


This seems like a real loss for the convenience of D.  One of the assets of the D language is that it makes a lot of things simple.  The example in the bug is fairly intuitive and easy to write.


> 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.

This restriction seems reasonable.  It'd be really, really tough to handle.  In my mind, the issue is that the : operator might make sense if the attribute: only applied to it's contained scope.  In this case, however, that doesn't really make sense, because version statements don't really have their own scope...  Otherwise, variable definitions inside of them wouldn't be available externally.
July 08, 2007
"Mike Parker" <aldacron71@yahoo.com> wrote in message news:f6pr67$2t00$1@digitalmars.com...
>
> 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?

No.  It's a form of conditional compilation, certainly, but it's not just like #ifdef.  Everything within a version block must be syntactically valid; "int" is not syntactically valid, so you can't put it in the version block.


July 08, 2007
"Jason House" <jason.james.house@gmail.com> wrote in message news:f6qq5d$1s0u$1@digitalmars.com...
>
> This seems like a real loss for the convenience of D.  One of the assets of the D language is that it makes a lot of things simple.  The example in the bug is fairly intuitive and easy to write.
>

I agree.  I think it's funny that none of the proposed solutions are anywhere near as simple or intuitive as the old, incorrect syntax.  Perhaps this is telling.


July 08, 2007
Should this work?

version(Windows)
{   const char[] external = "extern(Windows):"; }
else
{   const char[] external = "extern(C):"; }

mixin(external);
typedef void function() foo;

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;
> }
> -----------------
> 
> 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.
> 
> 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.
> 
> 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.
> 
> 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.
> 
> 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(); }
> 
> 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; }
July 08, 2007
Mike Wey wrote:
> Should this work?
> 
> version(Windows)
> {   const char[] external = "extern(Windows):"; }
> else
> {   const char[] external = "extern(C):"; }
> 
> mixin(external);
> typedef void function() foo;
> 

DMD 1.018 accepts that.  But maybe that's a bug too, since string mixins are supposed to be either an expression, a statement, or a declaration or definition?  According to the spec, it seems to be a valid AttributeSpecifier, which in turn is a valid DeclDef.  But Walter didn't suggest it as a workaround, which makes me worried.
July 08, 2007
torhu wrote:
> Mike Wey wrote:
>> Should this work?
>> 
>> version(Windows)
>> {   const char[] external = "extern(Windows):"; }
>> else
>> {   const char[] external = "extern(C):"; }
>> 
>> mixin(external);
>> typedef void function() foo;
>> 
> 
> DMD 1.018 accepts that.  But maybe that's a bug too, since string mixins are supposed to be either an expression, a statement, or a declaration or definition?  According to the spec, it seems to be a valid AttributeSpecifier, which in turn is a valid DeclDef.  But Walter didn't suggest it as a workaround, which makes me worried.

That the mixing trick works might be as bad as that this works:

---
version (Windows)
    extern (Windows):
---


Being able to insert 'extern (Windows):' using a string mixin seems equally bad to me.  Where's the important difference?
July 08, 2007
Walter Bright wrote:
> > 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.
> > 
> > 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.
> > 
> > 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.
> > 
> > 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(); }
> > 
> > 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; }

I don't like any of these solutions.  This is so straight forward with
the C preprocessor.  Even using the future macro feature means
duplicating definitions.

AFAIK, these problems only affect the linkage attributes, meaning
extern (C), extern (Windows), and export.

If Mike Wey's string mixin solution is allowed in future dmd versions, I
guess the situation isn't that bad.  As long as bug 1306 is fixed, anyway.

Another way would be to accept that this is a special case that needs a
pragmatic solution, and expand the syntax a bit:

extern (Windows, C):

According to the docs, A D compiler only need to support Windows and
Pascal conventions on Windows.  So on other platforms, the compiler
could just pick the first valid linkage for that platform.  A version ID
could be incorporated too, if there's ever a need for it:

extern (Windows : Windows; WeirdPlatform: WeirdLinkage; C):


The problem with 'export' is related.  I wonder if something similar
could work for that.  Make it conditional somehow:

export (DLLVERSION) int someVar;
export (version DLLVERSION) int someVar;
version (DLLVERSION)(export) int someVar;

The third version has the advantage of being applicable to other cases too:

version (Windows) (extern (Windows)) else (extern (C)) {
    /* openGL prototypes */
}


I don't know if this syntax is feasible, I'll leave that to the experts. 
 :)
July 08, 2007
Mike Wey wrote:
> Should this work?
> 
> version(Windows)
> {   const char[] external = "extern(Windows):"; }
> else
> {   const char[] external = "extern(C):"; }
> 
> mixin(external);
> typedef void function() foo;

No, it doesn't work (although it compiles).
« First   ‹ Prev
1 2 3 4 5 6
Top | Discussion index | About this forum | D home