July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

Adam D. Ruppe <destructionator@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |destructionator@gmail.com

--- Comment #11 from Adam D. Ruppe <destructionator@gmail.com> ---
One of the useful things about the current behavior would be mixing in a thing that needs a C callback function; the callback may be different for different instantiations.

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

--- Comment #12 from Manu <turkeyman@gmail.com> ---
And if there are extern(C) functions in the boilerplate, then ***THERE ARE
EXTERN(C) FUNCTIONS IN THE BOILERPLATE***. That's literally what we are talking
about!

You can't change the names of extern(C) functions, or they don't work.

There is absolutely no conceivable use for an extern(C) function that has been
mangled with a bizarre scheme that you can't even declare.
The function has been mangled, and there is literally no way to call it, or
produce a C declaration that matches the function name, because you can't know
what it is.

I'm telling you what extern(C) functions in mixin templates are for, and you're telling me I'm wrong, you don't accept practical real-world software as evidence if its value, and instead insist that this bizarre choice of renaming the function to an un-knowable name is absolutely the proper thing to do?

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

--- Comment #13 from Walter Bright <bugzilla@digitalmars.com> ---
One use case is to construct a C callback function, i.e. a pointer to a C function.

Another is to use C variadic function parameters.

Another is filling a table with C function pointers.

Another is providing an alias to a C function template parameter.

As I mentioned, the use case template mixins were designed for is for multiple inclusions of boilerplate. This won't work if the declarations conflict.

Besides, I've provided two ways you can get it to work for your case. Since you are only instantiating a mixin template exactly once, those ways will work.

It's why there is a pragma(mangle) feature. I suspect you're not manually
writing those template mixins, but generating them. Generating a pragma(mangle)
in them should not be a problem.

The string mixin shouldn't be a problem for your case, either.

At least, I don't understand why these would be problems.

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

--- Comment #14 from Manu <turkeyman@gmail.com> ---
I use those workarounds; but I still have to explain why it's broken to people.

Those cases you describe, which are all cases of effectively anonymous functions, should be the ones that use pragma(mangle), you can generate any string you like and stuff it in there, the name is totally irrelevant. But you're telling that I should generate the proper name in the default case, which is the only case where the name IS relevant.

C functions have naming requirements too; C functions need to have an underscore, or not, depending on the ABI. I shouldn't have to implement that logic to emit a C function name with or without underscore.

The case where extern(C) doesn't mean extern(C) (your proposed cases where you
want to emit many functions, which is distinctly NOT C) are the ones that
should be doing special work; it should require effort to violate the meaning
of extern(C), not the other way around.

I also bet there are precisely zero use cases as you describe in existence.

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

Ketmar Dark <ketmar@ketmar.no-ip.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ketmar@ketmar.no-ip.org

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

ponce <aliloko@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |aliloko@gmail.com

--- Comment #15 from ponce <aliloko@gmail.com> ---
Nice to see an explanation for this.

> You can also use:
> enum M = "export extern(C) void fun() {}";
> mixin(M);


We do use the string mixin workaround for years, it hasn't been a huge problem, just a minor surprise. Meh.

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

--- Comment #16 from anonymous4 <dfj1esp02@sneakemail.com> ---
Callbacks are more common than reflection-based dll exports. For some reason C++ doesn't allow nested extern "C" functions and they all get mangled.

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

--- Comment #17 from anonymous4 <dfj1esp02@sneakemail.com> ---
Also wouldn't you want to export them without the leading underscore?

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

--- Comment #18 from Manu <turkeyman@gmail.com> ---
> Callbacks are more common than reflection-based dll exports.

This is about what extern(C) means.
mixin is supposed to inject code into the lexical scope it's mixed into. If you
mixin to the global scope, it should be in the global scope, and there's
nothing to mangle with.
What we've learned here is that mixin is like some form of namespace, and
that's NOT written on the tin, and definitely not what I have ever wanted.
I bet almost nobody knows this, and they craft their mixins to work assuming
they that they are inserted into the scope they're mixed into.

This is also the part that doesn't make sense, an extern(C) function at global
scope should be an extern(C) function, even if you want to make the rule that a
nested one would be mangled somehow (since a nexted extern(C) function is kinda
invalid anyway), the global scoped function should be as you expect.

Callbacks nested in some scope are the thing that's distinctly not-C, and in
that scenario the name is effectively anonymous anyway, it could be anything.
That should be where you use the pragma(mangle) trick, with any string you
like, because it doesn't matter; `pragma(mangle, makeAnonName())`.

extern(C) should mean extern(C), and this case of "I half-want extern(C)" can
be deliberately achieved with pragma(mangle).

> Also wouldn't you want to export them without the leading underscore?

It depends on the ABI. To use pragma(mangle), you need to determinate the ABI details and mangle with or without the underscore accordingly.

--
July 24, 2019
https://issues.dlang.org/show_bug.cgi?id=20012

Dennis <dkorpel@live.nl> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |dkorpel@live.nl

--- Comment #19 from Dennis <dkorpel@live.nl> ---
(In reply to Manu from comment #18)
> What we've learned here is that mixin is like some form of namespace, and that's NOT written on the tin, and definitely not what I have ever wanted.

That's true indeed for template mixins. Not for string mixins btw, which is why extern(C) in string mixins does work like you want.

"The declarations in a mixin are placed in a nested scope and then ‘imported’ into the surrounding scope."

https://dlang.org/spec/template-mixin.html#mixin_scope

So this is not 'one weird case' where extern(C) works differently, it's really that the rule shouldn't require the declaration to _be_ in global scope, but _accessible_ from global scope, because then a user may want to call it from C.

Also, the spec says "C functions cannot be overloaded with another C function with the same name." (https://dlang.org/spec/interfaceToC.html), but with template mixins you can overload extern(C) functions currently.

If you need extern(C) functions for a pointer table, you can simply do:
```
mixin template foo() {
    alias CFunc = extern(C) void function(int);
    CFunc func = (int a) {printf("%d\n", a);};
}
```

No need to give it a D-mangled name when it's not supposed to be exposed.

> I bet almost nobody knows this, and they craft their mixins to work assuming they that they are inserted into the scope they're mixed into.

It does tend to be a surprise indeed. I first learned this when I wanted to mixin common opBinary functions and found that the specialized opBinary completely shadowed the mixed in ones. An alias or wrapper function needed to be added to make it work. It might be too late to change mixin template behavior though.

--