digitalmars.D - extern(C) symbol conflicts
- Steven Schveighoffer (50/50) Jan 26 2015 An interesting thing I learned while reading through some bug reports [1...
- Kagamin (7/7) Jan 27 2015 Does this work?
- Walter Bright (10/13) Jan 27 2015 D's interface to C and C++ does not adopt C and C++ semantics, in partic...
- Steven Schveighoffer (14/27) Jan 27 2015 I never said anything about C++. I don't see how that is relevant. C has...
- Jacob Carlborg (4/7) Jan 27 2015 It it's extern(C), shouldn't just the name matter?
- Steven Schveighoffer (4/9) Jan 27 2015 For instance, if one library tags it as pure, but another does not. I
- Jacob Carlborg (6/8) Jan 27 2015 Yeah. Do the compiler need to look at the parameters as well? Even if
- Walter Bright (6/12) Jan 27 2015 C does not have name mangling, so:
- Steven Schveighoffer (26/43) Jan 29 2015 Hah! so this actually makes me more convinced we should do it differentl...
- Artur Skawina via Digitalmars-d (12/14) Jan 29 2015 All functions are overloadable in D; ie you can mix C and D overloads
- Steven Schveighoffer (12/25) Jan 29 2015 I think I may not have expressed clearly, but I meant that C bindings
- Walter Bright (4/6) Jan 29 2015 My reason for saying "that's what C++ does" is that in 30 years I've nev...
- Steven Schveighoffer (29/35) Jan 30 2015 Maybe because that's not how it works in C++ (thought I would test):
- Walter Bright (1/1) Jan 30 2015 Try cfunction() with different parameter types.
- Steven Schveighoffer (5/6) Feb 02 2015 OK, I see that was your point.
- Jonathan Marler (21/27) Jan 29 2015 This extern(C) thing is a bit odd. With C, you can include as
An interesting thing I learned while reading through some bug reports [1]: cfile.c #include <stdio.h> void cfunction() {printf("hello\n");} file1.d module file1; extern(C) void cfunction(); file2.d module file2; extern(C) void cfunction(); main.d version(test1) { import file1; } version(test2) { import file2; } void main() { cfunction(); } cc -c cfile.c dmd -version=test1 main.d file1.d file2.d cfile.o ./main hello dmd -version=test2 main.d file1.d file2.d cfile.o ./main hello All fine and good. Now: dmd -version=test1 -version=test2 main.d file1.d file2.d cfile.o main.d(12): Error: file2.cfunction at file2.d(2) conflicts with file1.cfunction at file1.d(2) What gives here? cfunction is not part of any module, it's extern(C). In fact, both equate to the same symbol (as shown by the different ways we can import with only one implementation). But D considers them different. Why? I would have expected that any time you declare (but don't define) an extern(C) symbol, it's just like a prototype -- if it's already declared no big deal. But it shouldn't be module-based. Is there a good reason why we shouldn't allow the duplicate declaration in multiple modules? I understand for D symbols -- those are actually different symbols. This is actually a problem someone may encounter quite a bit -- 2 different libraries or even modules from the same library (see referenced bug) may create their own bindings to C functions. I would say, let's just let the linker figure it out, no? -Steve [1] https://issues.dlang.org/show_bug.cgi?id=7729
Jan 26 2015
Does this work? void main() { version(test1)test1.cfunction(); else test2.cfunction(); } All symbols belong to modules.
Jan 27 2015
On 1/26/2015 11:06 AM, Steven Schveighoffer wrote:Is there a good reason why we shouldn't allow the duplicate declaration in multiple modules? I understand for D symbols -- those are actually different symbols.D's interface to C and C++ does not adopt C and C++ semantics, in particular, it does not adopt C and C++ name lookup rules, function overloading rules, template instantiation rules, etc. This is on purpose to reduce the complexity of the language. As in C and C++, it is up to the D programmer to follow the One Definition Rule when interfacing with those languages. Declaring the same function in multiple modules is a bad idea. (Yes, I know, we had to bend that a bit to make C++ namespaces work, but we did as little as possible.)
Jan 27 2015
On 1/27/15 5:57 AM, Walter Bright wrote:On 1/26/2015 11:06 AM, Steven Schveighoffer wrote:I never said anything about C++. I don't see how that is relevant. C has no overloading rules or template instantiation rules.Is there a good reason why we shouldn't allow the duplicate declaration in multiple modules? I understand for D symbols -- those are actually different symbols.D's interface to C and C++ does not adopt C and C++ semantics, in particular, it does not adopt C and C++ name lookup rules, function overloading rules, template instantiation rules, etc. This is on purpose to reduce the complexity of the language.As in C and C++, it is up to the D programmer to follow the One Definition Rule when interfacing with those languages. Declaring the same function in multiple modules is a bad idea.The problem I see is that C bindings are easy to make. If I need to reference I function, I just declare it. If some other library writer needs the same function, he declares it. But now, if anyone wants to use both libraries, you have to pick one or the other, even though they are the same. I understand the idea behind keeping the lookup rules consistent. But C lookup rules ARE simple. I would say if two extern(C) declarations are identical (i.e. same parameter types, same attributes), they don't conflict. What does this break? -Steve
Jan 27 2015
On 2015-01-27 19:13, Steven Schveighoffer wrote:I would say if two extern(C) declarations are identical (i.e. same parameter types, same attributes), they don't conflict. What does this break?It it's extern(C), shouldn't just the name matter? -- /Jacob Carlborg
Jan 27 2015
On 1/27/15 3:11 PM, Jacob Carlborg wrote:On 2015-01-27 19:13, Steven Schveighoffer wrote:For instance, if one library tags it as pure, but another does not. I think an error in that case is warranted. -SteveI would say if two extern(C) declarations are identical (i.e. same parameter types, same attributes), they don't conflict. What does this break?It it's extern(C), shouldn't just the name matter?
Jan 27 2015
On 2015-01-27 21:31, Steven Schveighoffer wrote:For instance, if one library tags it as pure, but another does not. I think an error in that case is warranted.Yeah. Do the compiler need to look at the parameters as well? Even if you put const or immutable, it won't make difference on the C side. But it will probably be confusing if one is declared const and another is not. -- /Jacob Carlborg
Jan 27 2015
On 1/27/2015 11:22 PM, Jacob Carlborg wrote:On 2015-01-27 21:31, Steven Schveighoffer wrote:C does not have name mangling, so: extern(C) void foo(int); extern(C) void foo(char); will mangle to the same name, although D will regard them as different symbols. C++ treats extern"C" names the same way.For instance, if one library tags it as pure, but another does not. I think an error in that case is warranted.Yeah. Do the compiler need to look at the parameters as well? Even if you put const or immutable, it won't make difference on the C side. But it will probably be confusing if one is declared const and another is not.
Jan 27 2015
On 1/28/15 2:49 AM, Walter Bright wrote:On 1/27/2015 11:22 PM, Jacob Carlborg wrote:Hah! so this actually makes me more convinced we should do it differently: cfile.c #include <stdio.h> void cfunction() {printf("hello\n");} file1.d module file1; extern(C) void cfunction(int); file2.d module file2; extern(C) void cfunction(const char *); main.d import file1; import file2; void main() { cfunction(1); cfunction("1"); } This works, and prints "hello" twice. I really think D should consider extern(C) functions as not overloadable, and universally binding (i.e. it's not an error to have 2 identical definitions in separate modules, and it is an error to have 2 different overloads of an extern(C) fucntion). Is there a good reason to do it the current way besides "that's what C++ does"? -SteveOn 2015-01-27 21:31, Steven Schveighoffer wrote:C does not have name mangling, so: extern(C) void foo(int); extern(C) void foo(char); will mangle to the same name, although D will regard them as different symbols. C++ treats extern"C" names the same way.For instance, if one library tags it as pure, but another does not. I think an error in that case is warranted.Yeah. Do the compiler need to look at the parameters as well? Even if you put const or immutable, it won't make difference on the C side. But it will probably be confusing if one is declared const and another is not.
Jan 29 2015
On 01/29/15 14:43, Steven Schveighoffer via Digitalmars-d wrote:I really think D should consider extern(C) functions as not overloadable,All functions are overloadable in D; ie you can mix C and D overloads freely; this is a feature. It allows you to extend the C i/f without adding an extra layer of pointless wrappers. And export a subset of the D i/f to other C-but-not-D-aware languages. Etc.and universally binding (i.e. it's not an error to have 2 identical definitions in separate modules,External functions with identical signatures and identical mangled names are obviously not conflicting, so that case does not need to be an error, yes. But I suspect that, in practice, sticking to just one declaration is a good idea anyway (the language changes with every compiler release, so the signatures can easily get out of sync. eg missing nothrow/ nogc/ return attributes). artur
Jan 29 2015
On 1/29/15 1:35 PM, Artur Skawina via Digitalmars-d wrote:On 01/29/15 14:43, Steven Schveighoffer via Digitalmars-d wrote:I think I may not have expressed clearly, but I meant that C bindings should not be overloadable with C bindings. Overloading C functions with D functions is perfectly fine.I really think D should consider extern(C) functions as not overloadable,All functions are overloadable in D; ie you can mix C and D overloads freely; this is a feature. It allows you to extend the C i/f without adding an extra layer of pointless wrappers. And export a subset of the D i/f to other C-but-not-D-aware languages. Etc.In practice, this is more difficult to control. It's so easy to just throw in a C binding when you need it, especially if you don't know where an existing binding exists, or there isn't a reasonable central place to place it. This is more difficult when you have competing unrelated libraries that you need to use concurrently. To have one corrupt the other, when they are exactly the same binding, doesn't make a whole lot of sense. -Steveand universally binding (i.e. it's not an error to have 2 identical definitions in separate modules,External functions with identical signatures and identical mangled names are obviously not conflicting, so that case does not need to be an error, yes. But I suspect that, in practice, sticking to just one declaration is a good idea anyway (the language changes with every compiler release, so the signatures can easily get out of sync. eg missing nothrow/ nogc/ return attributes).
Jan 29 2015
On 1/29/2015 5:43 AM, Steven Schveighoffer wrote:Is there a good reason to do it the current way besides "that's what C++ does"?My reason for saying "that's what C++ does" is that in 30 years I've never heard anyone bring it up as a problem. So while we could go about diagnosing such errors, I regard it as a very low priority.
Jan 29 2015
On 1/29/15 4:38 PM, Walter Bright wrote:On 1/29/2015 5:43 AM, Steven Schveighoffer wrote:Maybe because that's not how it works in C++ (thought I would test): lib1.h: namespace x { extern "C" { void cfunction(); } } lib2.h: namespace y { extern "C" { void cfunction(); } } main.cpp: #include "lib1.h" #include "lib2.h" using namespace x; using namespace y; int main() { x::cfunction(); // OK y::cfunction(); // OK cfunction(); // OK return 0; } -SteveIs there a good reason to do it the current way besides "that's what C++ does"?My reason for saying "that's what C++ does" is that in 30 years I've never heard anyone bring it up as a problem. So while we could go about diagnosing such errors, I regard it as a very low priority.
Jan 30 2015
Try cfunction() with different parameter types.
Jan 30 2015
On 1/30/15 6:10 PM, Walter Bright wrote:Try cfunction() with different parameter types.OK, I see that was your point. But what do you say about C++ supporting what I suggested and D failing in that regard? -Steve
Feb 02 2015
On Tuesday, 27 January 2015 at 18:13:36 UTC, Steven Schveighoffer wrote:e or the other, even though they are the same.I understand the idea behind keeping the lookup rules consistent. But C lookup rules ARE simple. I would say if two extern(C) declarations are identical (i.e. same parameter types, same attributes), they don't conflict. What does this break? -SteveThis extern(C) thing is a bit odd. With C, you can include as many headers and declare the same function as many times as you want to. D is breaking this functionality. However, some may see this as a feature. Suppose you wanted to optimize where you were declaring your function prototypes by making sure there were no duplications? In C, I can't think of a way to do this. In D however, it looks like this would cause an error. I kinda agree that maybe D should handle it the same way C does, but another thought comes to mind. Say two libraries declared the same extern(C) function but they used different types or different type qualifiers. You would have to go to one of the libraries and have them modify their's to be identical to the other right? Well, the difference now is that instead of them normalizing their declarations you can suggest that everyone use the same modules for declaring extern(C) functions. This may have been impossible to do in an old language but since D is relatively new, this doesn't seem to unreasonable to do. Anyway, just some thoughts...I think this problem may be more complex then it seems.
Jan 29 2015