www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - version and extern problems

reply Walter Bright <newshound1 digitalmars.com> writes:
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; }
Jul 07 2007
next sibling parent reply Mike Parker <aldacron71 yahoo.com> writes:
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.
Jul 07 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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.
Jul 08 2007
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
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.
Jul 08 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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.
Jul 08 2007
prev sibling next sibling parent reply Mike Wey <no.spam example.com> writes:
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; }

Jul 08 2007
next sibling parent reply torhu <fake address.dude> writes:
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.
Jul 08 2007
parent torhu <fake address.dude> writes:
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?
Jul 08 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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).
Jul 08 2007
parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright 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;

No, it doesn't work (although it compiles).

That seems like it should work. Is the problem something to do with the order in which things are evaluated at compile-time? Sean
Jul 09 2007
next sibling parent Mike Wey <no.spam example.com> writes:
I guess that:

mixin("extern(C):");
typedef void function() foo;

is the same as:

extern(C){}
typedef void function() foo;

Sean Kelly wrote:
 Walter Bright 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;

No, it doesn't work (although it compiles).

That seems like it should work. Is the problem something to do with the order in which things are evaluated at compile-time? Sean

Jul 09 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 That seems like it should work.  Is the problem something to do with the 
 order in which things are evaluated at compile-time?

It doesn't work because mixin is supposed to be a complete statement, not part of one. The XXX: forms a prefix, not a complete statement.
Jul 09 2007
next sibling parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 That seems like it should work.  Is the problem something to do with 
 the order in which things are evaluated at compile-time?

It doesn't work because mixin is supposed to be a complete statement, not part of one.

Ah, I didn't know that. Thanks. SEan
Jul 09 2007
prev sibling parent Jason House <jason.james.house gmail.com> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 That seems like it should work.  Is the problem something to do with 
 the order in which things are evaluated at compile-time?

It doesn't work because mixin is supposed to be a complete statement, not part of one. The XXX: forms a prefix, not a complete statement.

I thought "gotolabel:" would be a prefix but "private:" isn't. Is there a big reason that distinction isn't true?
Jul 09 2007
prev sibling next sibling parent torhu <fake address.dude> writes:
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. :)
Jul 08 2007
prev sibling next sibling parent reply Russell Lewis <webmaster villagersonline.com> writes:
This sounds like the perfect place for alias, or something like it.  The 
idea would be to write code like this:

--------------
version(Windows)
   <do alias declaration here>;
else
   <do other alias delcaration here>;

extern(myAlias) typedef void function() foo;
--------------

Problem is, I haven't yet found a syntax that I like.  I came up with 3 
possibilities:

1) alias extern(Windows) extern(myAlias);

I don't like the extern(mine) part, because it reminds me too much of C 
function pointers.  That was a good example of a nice theoretical syntax 
that never worked in practice, IMHO.

Plus, this syntax could easily be read as "extern(Windows) attribute on 
the type extern(mine)..."  Technically, I guess the grammar's not 
ambiguous, but it's ugly.

2) alias extern(Windows) myAlias;

I don't like how this breaks the parallelism.  Would you use the alias 
extern(myAlias)?  Or would you drop the extern() part around it?  If the 
latter, how would you parse it without ambiguities?

Plus, the same problem as before where it looks like extern(Windows) is 
an attribute on something else.

3) alias Windows myAlias;

I presume that Windows isn't a valid symbol, so this has its own problems.


Thoughts?  About the concept in general, or about a good syntax?

Russ

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

Jul 08 2007
next sibling parent =?ISO-8859-1?Q?Julio_C=E9sar_Carrascal_Urquijo?= writes:
Russell Lewis wrote:
 This sounds like the perfect place for alias, or something like it.  The 
 idea would be to write code like this:
 

(...)
 
 2) alias extern(Windows) myAlias;
 
 I don't like how this breaks the parallelism.  Would you use the alias 
 extern(myAlias)?  Or would you drop the extern() part around it?  If the 
 latter, how would you parse it without ambiguities?
 
 Plus, the same problem as before where it looks like extern(Windows) is 
 an attribute on something else.

This is exactly what I thought while reading the OP. Maybe it would be clearer if the scope syntax is reused for alias: version (Windows) alias(extern) Windows MyAlias; else alias(extern) C MyAlias; extern(MyAlias): void foo(); void bar(); ...
Jul 08 2007
prev sibling parent BCS <ao pathlink.com> writes:
Reply to Russell,

 This sounds like the perfect place for alias, or something like it.
 The idea would be to write code like this:
 
 --------------
 version(Windows)
 <do alias declaration here>;
 else
 <do other alias delcaration here>;
 extern(myAlias) typedef void function() foo;
 --------------
 Problem is, I haven't yet found a syntax that I like.  I came up with
 3 possibilities:
 
 1) alias extern(Windows) extern(myAlias);
 

how about alow extern to take a string? const char[] type = "Windows"; extern(type);
Jul 08 2007
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
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.

Unfortunately (b) is extremely common, including on some significant libraries. (eg, MySQL). We really need a solution to this.
Jul 09 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Don Clugston wrote:
 Unfortunately (b) is extremely common, including on some significant 
 libraries.
 (eg, MySQL).

I know. I was just hoping to head off *new* code being written that way.
 We really need a solution to this.

I proposed several, they should work.
Jul 09 2007
prev sibling parent reply Georg Wrede <georg nospam.org> writes:
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;

Why not extern(ecc) void function() foo; where ecc would resolve to Windows or C. This of course requires some tweaking of the compiler, but it might be worth it.
Jul 09 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Georg Wrede wrote:
 Why not
 
   extern(ecc) void function() foo;
 
 where ecc would resolve to Windows or C.
 
 This of course requires some tweaking of the compiler, but it might be 
 worth it.

Do you mean "ecc" being a magic identifier that the compiler sets to Windows on Windows, and C otherwise? That might be a pretty good idea. Why "ecc", though? It doesn't jump out at me what it might mean. How about "System" ?
Jul 09 2007
next sibling parent reply Carlos Santander <csantander619 gmail.com> writes:
Walter Bright escribió:
 Georg Wrede wrote:
 Why not

   extern(ecc) void function() foo;

 where ecc would resolve to Windows or C.

 This of course requires some tweaking of the compiler, but it might be 
 worth it.

Do you mean "ecc" being a magic identifier that the compiler sets to Windows on Windows, and C otherwise? That might be a pretty good idea. Why "ecc", though? It doesn't jump out at me what it might mean. How about "System" ?

I'm guessing this would be the default calling convention for the system. If the system was written in C++ (BeOS?), would it have to mean "extern(C++)"? If someone wrote an OS in D, would it just be "extern(D)"? Or would it just be specified that "System" means "Windows" on Windows and "C" everywhere else? -- Carlos Santander Bernal
Jul 09 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Carlos Santander wrote:
 I'm guessing this would be the default calling convention for the 
 system. If the system was written in C++ (BeOS?), would it have to mean 
 "extern(C++)"? If someone wrote an OS in D, would it just be 
 "extern(D)"? Or would it just be specified that "System" means "Windows" 
 on Windows and "C" everywhere else?

No, it would be what is necessary to interface to packages that pick pointless things like "Windows" calling conventions. If lots of C packages used "FooBar" calling conventions on XXX, then it would mean "FooBar" on that system.
Jul 09 2007
next sibling parent Carlos Santander <csantander619 gmail.com> writes:
Walter Bright escribió:
 Carlos Santander wrote:
 I'm guessing this would be the default calling convention for the 
 system. If the system was written in C++ (BeOS?), would it have to 
 mean "extern(C++)"? If someone wrote an OS in D, would it just be 
 "extern(D)"? Or would it just be specified that "System" means 
 "Windows" on Windows and "C" everywhere else?

No, it would be what is necessary to interface to packages that pick pointless things like "Windows" calling conventions. If lots of C packages used "FooBar" calling conventions on XXX, then it would mean "FooBar" on that system.

Ok, thanks. -- Carlos Santander Bernal
Jul 09 2007
prev sibling parent Russell Lewis <webmaster villagersonline.com> writes:
Walter Bright wrote:
 Carlos Santander wrote:
 I'm guessing this would be the default calling convention for the 
 system. If the system was written in C++ (BeOS?), would it have to 
 mean "extern(C++)"? If someone wrote an OS in D, would it just be 
 "extern(D)"? Or would it just be specified that "System" means 
 "Windows" on Windows and "C" everywhere else?

No, it would be what is necessary to interface to packages that pick pointless things like "Windows" calling conventions. If lots of C packages used "FooBar" calling conventions on XXX, then it would mean "FooBar" on that system.

By this argument, it could just as easily be called "Broken". I am concerned that calling it "System" will encourage widespread use...which is a step in exactly what you said was the wrong direction. Did you see the post about using strings for extern? That seemed like an elegant solution to me: version(Windows) const char[] myExternAlias = "Windows"; else const char[] myExternAlias = "C"; extern(myExternAlias) void foo();
Jul 10 2007
prev sibling next sibling parent Extrawurst <spam extrawurst.org> writes:
i like this idea !

/signed



Walter Bright schrieb:
 Georg Wrede wrote:
 Why not

   extern(ecc) void function() foo;

 where ecc would resolve to Windows or C.

 This of course requires some tweaking of the compiler, but it might 
 be worth it.

Do you mean "ecc" being a magic identifier that the compiler sets to Windows on Windows, and C otherwise? That might be a pretty good idea. Why "ecc", though? It doesn't jump out at me what it might mean. How about "System" ?

Jul 09 2007
prev sibling next sibling parent reply Mike Parker <aldacron gmail.com> writes:
Walter Bright wrote:
 Georg Wrede wrote:
 Why not

   extern(ecc) void function() foo;

 where ecc would resolve to Windows or C.

 This of course requires some tweaking of the compiler, but it might be 
 worth it.

Do you mean "ecc" being a magic identifier that the compiler sets to Windows on Windows, and C otherwise? That might be a pretty good idea. Why "ecc", though? It doesn't jump out at me what it might mean. How about "System" ?

Please :) This looks like a great solution. And extern(System) is intuitive.
Jul 10 2007
parent reply torhu <fake address.dude> writes:
Mike Parker wrote:
 Walter Bright wrote:
 Georg Wrede wrote:
 Why not

   extern(ecc) void function() foo;

 where ecc would resolve to Windows or C.

 This of course requires some tweaking of the compiler, but it might be 
 worth it.

Do you mean "ecc" being a magic identifier that the compiler sets to Windows on Windows, and C otherwise? That might be a pretty good idea. Why "ecc", though? It doesn't jump out at me what it might mean. How about "System" ?

Please :) This looks like a great solution. And extern(System) is intuitive.

Yes, but what the Pascal convention? And the windows API uses more then those three conventions, too. This system isn't very flexible in case other platforms start using more than one convention. Maybe Windows and Pascal could just 'degrade' into C on other platforms than windows? Or allowing several possibilities: extern (Windows, C) Windows is picked on Windows, C on other platforms. This allows you to control what happens.
Jul 10 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
torhu wrote:
 Yes, but what the Pascal convention?

People who #ifdef their C code to use Pascal on one platform and C on another should be excommunicated, and their software should be stricken from all monuments.
Jul 10 2007
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Georg Wrede wrote:
 Why not

   extern(ecc) void function() foo;

 where ecc would resolve to Windows or C.

 This of course requires some tweaking of the compiler, but it might be 
 worth it.

Do you mean "ecc" being a magic identifier that the compiler sets to Windows on Windows, and C otherwise? That might be a pretty good idea. Why "ecc", though? It doesn't jump out at me what it might mean. How about "System" ?

IMHO, extern(System) would be fantastic.
Jul 10 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Don Clugston wrote:
 IMHO, extern(System) would be fantastic.

It's a kludge, but a very useful one. I'll fold it into the next update.
Jul 10 2007
prev sibling next sibling parent reply "Chris Miller" <chris dprogramming.com> writes:
On Mon, 09 Jul 2007 14:01:11 -0400, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 Georg Wrede wrote:
 Why not
    extern(ecc) void function() foo;
  where ecc would resolve to Windows or C.
  This of course requires some tweaking of the compiler, but it might =


 worth it.

Do you mean "ecc" being a magic identifier that the compiler sets to =

 Windows on Windows, and C otherwise? That might be a pretty good idea.=

 Why "ecc", though? It doesn't jump out at me what it might mean.

 How about "System" ?

I don't know; why not let it just be a string literal... version(Windows) const char[] mylibcall =3D "Windows"; else const char[] mylibcall =3D "C"; extern(mylibcall) void function() foo;
Jul 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Chris Miller wrote:
 I don't know; why not let it just be a string literal...
 
 version(Windows)
     const char[] mylibcall = "Windows";
 else
     const char[] mylibcall = "C";
 
 extern(mylibcall) void function() foo;

It's the old chicken-and-egg problem of forward references.
Jul 10 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:f70mvo$22e7$1 digitalmars.com...
 It's the old chicken-and-egg problem of forward references.

Mind elaborating? I can't see how this is a forward reference issue.
Jul 10 2007
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Jarrett,

 "Walter Bright" <newshound1 digitalmars.com> wrote in message
 news:f70mvo$22e7$1 digitalmars.com...
 
 It's the old chicken-and-egg problem of forward references.
 


extern(A) const char[] A = "foo"; but as long as you don't worry about symbol names untill you are making .o files that isn't a problem (I would think).
Jul 10 2007
parent "Chris Miller" <chris dprogramming.com> writes:
On Tue, 10 Jul 2007 16:55:47 -0400, BCS <ao pathlink.com> wrote:

 Reply to Jarrett,

 "Walter Bright" <newshound1 digitalmars.com> wrote in message
 news:f70mvo$22e7$1 digitalmars.com...

 It's the old chicken-and-egg problem of forward references.




extern(A) const char[] A =3D "foo"; but as long as you don't worry about symbol names untill you are makin=

 .o files that isn't a problem (I would think).

I think problems like that exist all over the place; just don't do it. int foo =3D foo.init; I would like to know what Walter is referring to too.
Jul 10 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jarrett Billingsley wrote:
 "Walter Bright" <newshound1 digitalmars.com> wrote in message 
 news:f70mvo$22e7$1 digitalmars.com...
 It's the old chicken-and-egg problem of forward references.

Mind elaborating? I can't see how this is a forward reference issue.

Sure. What if the string is a const string defined after it is used?
Jul 10 2007
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Walter,

 Jarrett Billingsley wrote:
 
 "Walter Bright" <newshound1 digitalmars.com> wrote in message
 news:f70mvo$22e7$1 digitalmars.com...
 
 It's the old chicken-and-egg problem of forward references.
 



That is one thing I have never understood: why does lexical order ever (outside of statement lists e.i. function) ever cause an issue? I would expect that all of these issues could be removed by having one pass over the AST find the symbols (without getting types or anything else). Then another pass starts filling them out by reclusively evaluating symbols. The only issue I see right off involve non-determinism of some things like static asserts and ifs and how errors are found. But I think that that could be removed (or rendered irrelevant) for cases where errors aren't present. Then again, my experience with compilers stops just barely after lexers. However I /am/ genuinely interested in your thoughts on this.
Jul 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
BCS wrote:
 That is one thing I have never understood: why does lexical order ever 
 (outside of statement lists e.i. function) ever cause an issue?

const string x = "abc" ~ foo; const string foo = x[1..4];
Jul 10 2007
next sibling parent reply nonnymouse <nonnymouse nonnymouse.hole> writes:
Walter Bright Wrote:

 BCS wrote:
 That is one thing I have never understood: why does lexical order ever 
 (outside of statement lists e.i. function) ever cause an issue?

const string x = "abc" ~ foo; const string foo = x[1..4];

Surely that's just an "x is not defined on line 2" error?
Jul 10 2007
parent reply 0ffh <spam frankhirsch.net> writes:
nonnymouse wrote:
 Surely that's just an "x is not defined on line 2" error?

Nope. This, for example, is a perfectly valid D programme: ---<snip>--- module test; int y=3+x; const int x=5; void main() { printf("x=%i y=%i\n",x,y); } ---<snap>--- Seems like D collects all declarations first, unlike C. Regards, Frank
Jul 11 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
0ffh wrote:
 nonnymouse wrote:
 Surely that's just an "x is not defined on line 2" error?

Nope. This, for example, is a perfectly valid D programme: ---<snip>--- module test; int y=3+x; const int x=5; void main() { printf("x=%i y=%i\n",x,y); } ---<snap>--- Seems like D collects all declarations first, unlike C. Regards, Frank

The snippet you snipped was: const string x = "abc" ~ foo; const string foo = x[1..4]; So more like const int y=3+x; const int x=5+y; No matter which order you scan that, you can't come to a valid order of initialization. --bb
Jul 11 2007
parent 0ffh <spam frankhirsch.net> writes:
Bill Baxter wrote:
 So more like
 const int y=3+x;
 const int x=5+y;
 
 No matter which order you scan that, you can't come to a valid order of 
 initialization.

I get that, really, I do. I just wanted to point out why 'Surely that's just an "x is not defined on line 2" error?' does not hold, nor is to the point (which you just repeated). Regards, frank
Jul 11 2007
prev sibling parent reply nonnymouse <nonnymouse nonnymouse.hole> writes:
0ffh Wrote:

 Nope. This, for example, is a perfectly valid D program:

Then it must be "foo is not defined on line 1". ;-) I think maybe you thought I said line 1 last time. I said line 2. I was assuming that the compiler had looked ahead and come to a dead-end (or more expressively: a cul-de-sac). (We are assuming that is either the whole program or the only lines that matter here are we?) At some point, even if it is just before code generation all these symbols need to be defined. The compiler must choose a, possibly arbitrary, order in which it fills them. Surely when going through the AST to do this for a particular symbol, if it meets itself further down the tree it throws an error. I chose a sarcastically vague one.. "Symbol x cannot de defined as definition of x is based on definition of x" might make a bit more sense. Please, if possible, read this as a question rather than an answer. i.e I would genuinely like to be corrected where I have misunderstood (or missed out) some of the complexities of these issues.
Jul 11 2007
parent 0ffh <spam frankhirsch.net> writes:
nonnymouse wrote:
 0ffh Wrote:
 
 Nope. This, for example, is a perfectly valid D program:

I think maybe you thought I said line 1 last time. I said line 2. I was assuming that the compiler had looked ahead and come to a dead-end (or more expressively: a cul-de-sac). (We are assuming that is either the whole program or the only lines that matter here are we?)

Oops, now I get your drift. I was indeed misunderstanding you! I think one might rather choose to call it a circular or recursive problem...
 At some point, even if it is just before code generation all these symbols
need to be defined.
 [...] "Symbol x cannot de defined as definition of x is based on definition of
x" might make a bit more sense.
 Please, if possible, read this as a question rather than an answer. i.e I
would genuinely like to be 
 corrected where I have misunderstood (or missed out) some of the complexities
of these issues.

Well, I think the gist of your explanation is right, even if we cannot know about the actual implementation details. Regards, frank p.s. Funny thing, I don't get you; and in turn Bill doesn't get me! That's Karma, if not coincidence! ;-)
Jul 11 2007
prev sibling next sibling parent "Chris Miller" <chris dprogramming.com> writes:
On Tue, 10 Jul 2007 20:24:23 -0400, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 BCS wrote:
 That is one thing I have never understood: why does lexical order eve=


 (outside of statement lists e.i. function) ever cause an issue?

const string x =3D "abc" ~ foo; const string foo =3D x[1..4];

This is always an issue and should cause an error, if extern uses string= s = or not. How does this really pertain to extern that doesn't cause proble= ms = even without the requested feature?
Jul 10 2007
prev sibling parent BCS <ao pathlink.com> writes:
Reply to Walter,

 BCS wrote:
 
 That is one thing I have never understood: why does lexical order
 ever (outside of statement lists e.i. function) ever cause an issue?
 

const string foo = x[1..4];

That has little or nothing to do with lexical order. Baring retro-order dependencies does remove this type of error, but it also bars otherwise valid code. I would propose that the evaluation could run like this: use rand() to pick something to start with (not needed but it would work anyway) attempt to evaluate value of foo foo requires x attempt to evaluate value of x x requires foo we have started but not finished evaluating x therefor "Error: cyclical dependency for foo" The same model could be used for auto typing, template evaluation or whatever. As long as you never have to evaluate something as part of evaluating itself, order can be ignored.
Jul 10 2007
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:f70vtc$opn$1 digitalmars.com...
 Jarrett Billingsley wrote:
 Mind elaborating?  I can't see how this is a forward reference issue.

Sure. What if the string is a const string defined after it is used?

Then fail. It still wouldn't prevent the much more sensible and commond case of defining the string _before_ the extern.
Jul 10 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Jarrett Billingsley wrote:
 "Walter Bright" <newshound1 digitalmars.com> wrote in message 
 news:f70vtc$opn$1 digitalmars.com...
 Jarrett Billingsley wrote:
 Mind elaborating?  I can't see how this is a forward reference issue.


Then fail. It still wouldn't prevent the much more sensible and commond case of defining the string _before_ the extern.

There are several bug reports marked as important in bugzilla where people are very desirous of using things lexically before they are defined. I do not wish to add to it.
Jul 10 2007
prev sibling parent reply Georg Wrede <georg nospam.org> writes:
Walter Bright wrote:
 Georg Wrede wrote:
 
 Why not

   extern(ecc) void function() foo;

 where ecc would resolve to Windows or C.

 This of course requires some tweaking of the compiler, but it might be 
 worth it.

Do you mean "ecc" being a magic identifier that the compiler sets to Windows on Windows, and C otherwise? That might be a pretty good idea. Why "ecc", though? It doesn't jump out at me what it might mean.

I wanted the discussion to stay with the (dis)merits of the idea itself, and not (as usual) begin with bickering about the choice of word. For a similar reason I avoided taking a stance on whether ecc should be automatic, user settable or user overridable. (Pascal comes to mind, but you murdered that in a follow-up post. ;-) ) BTW, what are the implications of an automatic-only ecc for cross compiling with gdc? (I thought it was obvious: ecc for External Calling Convention. :-) )
 How about "System" ?

I'm not entirely opposed to System. Still, System is a big word.
Jul 11 2007
parent reply Derek Parnell <derek psyc.ward> writes:
On Wed, 11 Jul 2007 15:27:44 +0300, Georg Wrede wrote:

 How about "System" ?

I'm not entirely opposed to System. Still, System is a big word.

Especially when there are so many great keywords that we can further overload <G> extern(auto) extern(default) extern(static) extern(type) extern(override) . . . -- Derek Parnell Melbourne, Australia "Down with mediocrity!"
Jul 11 2007
parent Derek Parnell <derek psyc.ward> writes:
On Wed, 11 Jul 2007 22:47:32 +1000, Derek Parnell wrote:

 On Wed, 11 Jul 2007 15:27:44 +0300, Georg Wrede wrote:
 
 How about "System" ?

I'm not entirely opposed to System. Still, System is a big word.

Especially when there are so many great keywords that we can further overload <G> extern(auto) extern(default) extern(static) extern(type) extern(override) . . .

Damn ... I forgot a couple ... extern(extern) extern() -- Derek Parnell Melbourne, Australia "Down with mediocrity!"
Jul 11 2007