Jump to page: 1 2
Thread overview
extern(C) symbol conflicts
Jan 27, 2015
Kagamin
Jan 27, 2015
Walter Bright
Jan 27, 2015
Jacob Carlborg
Jan 28, 2015
Jacob Carlborg
Jan 28, 2015
Walter Bright
Jan 29, 2015
Artur Skawina
Jan 29, 2015
Walter Bright
Jan 30, 2015
Walter Bright
Jan 29, 2015
Jonathan Marler
January 26, 2015
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
January 27, 2015
Does this work?

void main()
{
   version(test1)test1.cfunction();
   else test2.cfunction();
}

All symbols belong to modules.
January 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.)
January 27, 2015
On 1/27/15 5:57 AM, Walter Bright wrote:
> 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.

I never said anything about C++. I don't see how that is relevant. C has no overloading rules or template instantiation rules.

>
> 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
January 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
January 27, 2015
On 1/27/15 3:11 PM, Jacob Carlborg wrote:
> 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?
>

For instance, if one library tags it as pure, but another does not. I think an error in that case is warranted.

-Steve
January 28, 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
January 28, 2015
On 1/27/2015 11:22 PM, Jacob Carlborg wrote:
> 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.
>

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.
January 29, 2015
On 1/28/15 2:49 AM, Walter Bright wrote:
> On 1/27/2015 11:22 PM, Jacob Carlborg wrote:
>> 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.
>>
>
> 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.

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"?

-Steve
January 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
« First   ‹ Prev
1 2