With clang and libstdc++ on Linux, is it currently feasible to use any standard library types in a module interface?
Asked Answered
A

1

6

So far it seems to me that including almost any libstdc++ header in a C++ module interface causes compile errors on clang 14.0.0 and the libstdc++ that comes bundled with GCC 11.2.0. I wonder if I am doing something wrong or if this is just not something that is supported yet. (I see that the Clang modules support is "partial", but haven't been able to find what is implemented and what is not.)

Here's a trivial module example that I got to work with clang-14 in Linux, linked with libstdc++. It demonstrates that libstdc++ headers can be used in a module implementation, but this example does not #include anything in the module interface:

// mod_if.cc
export module mod;
export int foo();

// mod.cc
module;
#include <iostream>
module mod;
int foo() {
    std::cout << "Hello world from foo()" << std::endl;
    return 42;
}

// use.cc
import mod;
#include <iostream>

int main() {
    std::cout << foo() << std::endl;
}

This works:

$ CXXFLAGS="-std=c++20 -fmodules -fprebuilt-module-path=prebuilt"
$ clang++ -c $CXXFLAGS -Xclang -emit-module-interface -o prebuilt/mod.pcm mod_if.cc
$ clang++ -c $CXXFLAGS -fmodule-file=prebuilt/mod.pcm mod.cc -o mod.o
$ clang++ $CXXFLAGS use.cc mod.o prebuilt/mod.pcm -o use
$ ./use 
Hello world from foo()
42

However, suppose I wanted foo to return a std::string:

// mod_if.cc
module;
#include <string>
export module mod;
export std::string foo();

// mod.cc
module;
#include <string>
module mod;
std::string foo() {
    return "42";
}

// no use.cc needed since the error happens when building mod.cc

This does not compile (first of many similar errors shown):

$ clang++ -c $CXXFLAGS -Xclang -emit-module-interface -o prebuilt/mod.pcm mod_if.cc
$ clang++ -c $CXXFLAGS -fmodule-file=prebuilt/mod.pcm mod.cc -o mod.o
In file included from mod.cc:2:
In file included from /usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/string:40:
In file included from /usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/char_traits.h:39:
In file included from /usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_algobase.h:64:
In file included from /usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_pair.h:65:
/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/compare:348:33: error: redefinition of '__cmp_cat_id<std::partial_ordering>'
      inline constexpr unsigned __cmp_cat_id<partial_ordering> = 2;
                                ^
/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_pair.h:65:11: note: '/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/compare' included multiple times, additional include site in header from module 'mod.<global>'
# include <compare>
          ^
/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_pair.h:65:11: note: '/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/compare' included multiple times, additional include site in header from module '<global>'
# include <compare>
          ^
mod.cc:1:1: note: <global> defined here
module;
^

Is there currently a way to make this code work (without resorting to writing module maps for the libstdc++ headers)? Why does this error happen? It sounds strange that the inline constexpr declaration included in the global module fragment gets exported, but then I don't claim to understand modules well.

Ardie answered 24/4, 2022 at 10:44 Comment(2)
Did you manage to found an answer for this? I am facing the same problem using GCC headers throght MinGW in Windows. In Unix have no problems, but linking against libc++Beitnes
Yes. More or less, anyway. I wrote it as an answer. :)Ardie
A
1

Ok, this is something that sort of worked for a large project. Note that this was half a year ago, so the world may have moved on.

I ended up creating a single header, "sys.hh", that #includes pretty much all the system headers used in the project. What seems to be important is that nothing directly or indirectly #included by this file gets #included directly or indirectly (outside the module system) in anything that gets linked into the final binary.

My "sys.hh" looks something like this:

#include <algorithm>
#include <array>
#include <assert.h>
#include <atomic>
#include <bits/std_abs.h>
// 100+ lines omitted, including things like glib, gtk, libjpeg
#include <vector>
#include <x86intrin.h>
#include <zlib.h>

// Macros won't get exported, so whatever the code needs, redefine as
// constexpr (or consteval functions) here. Unfortunately, I don't think
// there's a way to retain the name of the macro; so add an underscore.
// Also put them in a namespace.
#define MEXP(X) constexpr auto X ## _ = X;

namespace sys {
    MEXP(INTENT_PERCEPTUAL);
    MEXP(INTENT_RELATIVE_COLORIMETRIC);
    MEXP(INTENT_SATURATION);
    MEXP(INTENT_ABSOLUTE_COLORIMETRIC);
    MEXP(G_PRIORITY_DEFAULT_IDLE);
}

And my my modulemap file contains an entry like this:

module sys {
    header "prebuilt/sys.hh"
    use _Builtin_intrinsics
    export *
}

Compiling this header/module is a bit of an incremental process; you will run into modules that fail to compile because they indirectly include the same headers, so you add them into this file and rebuild until it works.

Note that managing build dependencies becomes much more of a thing with modules. At least half a year ago no good (automatic) ways seemed to exist to discover what needs to be rebuilt. This is made trickier by the fact that the name of the module does not tell where it lives in the source code.

Ardie answered 17/10, 2022 at 22:19 Comment(2)
I applied the same solution days ago for a custom build system for modern C++ projects. But, I am still unable to rid out of the redefinition problems. Are you saying that macros are causing the problems? Header guards and stuff like that?Beitnes
No, macros are a separate issue, you just need to be aware that modules don't export them. Any redefinition will cause a problem. You need to make sure that your project doesn't #include anything from outside except in the sys module. You could replace all #includes to outside in all the modules with "import sys" to make it easier to be sure you have got rid of all of them.Ardie

© 2022 - 2024 — McMap. All rights reserved.