[cfe-dev] C++ modules imported in extern "C"

Seth Cantrell seth.cantrell at gmail.com
Thu Oct 2 13:43:54 PDT 2014


On Oct 2, 2014, at 1:39 PM, Richard Smith <richard at metafoo.co.uk> wrote:

On Thu, Oct 2, 2014 at 7:21 AM, Seth Cantrell <seth.cantrell at gmail.com>
wrote:

>
> On Oct 2, 2014, at 1:12 AM, Richard Smith <richard at metafoo.co.uk> wrote:
>
> On Wed, Oct 1, 2014 at 7:54 PM, Seth Cantrell <seth.cantrell at gmail.com>
> wrote:
>
>> With C++ modules being far enough along to be included in -fmodules now I
>> tried enabling -fmodules on a small C++ program I have laying around. I was
>> impressed that it almost all worked right away.
>>
>> Here's the issue I ran into with one header:
>>
>>
>> glcorearb.h:616:1: error: import of C++ module 'Darwin.C.stddef' appears
>> within extern "C" language linkage specification
>> #include <stddef.h>
>> ^
>> glcorearb.h:5:1: note: extern "C" language linkage specification begins
>> here
>> extern "C" {
>> ^
>> glcorearb.h:1517:1: error: import of C++ module 'Darwin.C.inttypes'
>> appears within extern "C" language linkage specification
>> #include <inttypes.h>
>> ^
>> glcorearb.h:5:1: note: extern "C" language linkage specification begins
>> here
>> extern "C" {
>> ^
>>
>>
>> Even without modules it seems like people should be taking care not
>> #include a header in a context that may not be compatible with the content
>> of those headers. Here we are building C++ and stddef.h and inttypes.h are
>> therefore C++ headers that might not work in an extern "C" context.
>> Platforms owners, who are in control of both the including file and the
>> included file can legitimately do this, but I don't think this is good
>> practice when included someone else's header. However the standards don't
>> clearly disallow this
>
>
> C++ absolutely does disallow it for its own headers (which include
> <stddef.h> and <inttypes.h>):
>
> [using.headers]p3: "A translation unit shall include a header only outside
> of any external declaration or definition, and shall include the header
> lexically before the first reference in that translation unit to any of the
> entities declared in that header. No diagnostic is required.”
>
>
> Oh, excellent. That means I can report it to the header’s devs as a bug
> with a nice standards quote. Thanks.
>
>
>
> and I have certainly seen circumstances where people recommended extern
>> "C" { #include <system header> } for various reasons.
>>
>> In this specific case it's not even deliberate as far as I can tell, but
>> it's worked and so no one's bothered to fix it here. A quick hack to remove
>> these #includes from extern "C" solved the issue for me.
>>
>> Anyway, I checked
>> http://clang.llvm.org/docs/Modules.html#modularizing-a-platform but
>> didn't notice any recommendations that apply here. So what should be
>> recommended? Is this an 'anti-pattern' for modules? An anti-pattern for
>> 'well organized' non-module headers? Should modules be able to coexist with
>> these kinds of shenanigans when the code worked fine pre-modules? Should I
>> contact the developers of this header and recommend a change? (It's an
>> automatically generated header from gl3w.)
>
>
> See http://clang.llvm.org/docs/Modules.html#module-declaration and in
> particular the description of the [extern_c] attribute. By default, a
> module that can be used in C++ can only be used at global scope (outside of
> all namespaces and language linkage specifications). This attribute says
> that the module provides a C interface to C++ code, and thus:
> 1) The module is built surrounded by an implicit extern "C" context, and
> 2) The module can be imported within a top-level extern "C" linkage
> specification.
>
> This attribute should typically be applied to modules whose headers
> contain the
>
> #ifdef __cplusplus
> extern "C" {
> #endif
> // ...
> #ifdef __cplusplus
> }
> #endif
>
> pattern. Depending on your platform, it may or may not make sense to use
> the [extern_c] on your libc module; Darwin's doesn't (probably because its
> module map predates this attribute) but my glibc module map does.
>
>
> Would it make sense for the compiler, in the interest of backwards
> compatibility, to just go ahead and import a module ignoring the fact that
> it’s imported in an extern “C” context?
>

That breaks things. Suppose you have a C header that does *not* include the
above conditional 'extern "C" {' shenanigans. From C++ code, you'd use that
library like this:

extern "C" {
  #include "that_library.h"
}

If you supply a module map for that library and you add neither 'requires
!cplusplus' nor '[extern_c]', *and* we follow your suggestion, then we'd
build that_library.h has a non-extern-C C++ header file, which will lead to
weird link errors.


Doesn't providing such a module map constitute an incorrect assertion that
the header works as a C++ module, and so an error there would be okay?

I think a more common situation will be people just flipping on -fmodules
without providing module maps for all their headers, so for them extern "C"
{ #include "that_library.h" } will work just fine. And if they do get
around to writing their own module maps they should correctly mark C
modules as C.

Of course if my system's module map used the extern_c attribute there'd be
no problem either way, so I agree that's the real solution. I'll file a bug
with Apple for this as well.

And here's the bug I filed with Khronos for their header:
https://www.khronos.org/bugzilla/show_bug.cgi?id=1236


And we can't link the implicit "extern "C" { ... }" to the 'requires' line
because there are also libraries that provide a richer interface when
included from C++ (they don't just wrap everything in extern "C"
themselves). The best option here seems to be to require module maps to
explicitly specify this property.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20141002/6eaf6fa8/attachment.html>


More information about the cfe-dev mailing list