【问题标题】:g++ 11.2.0 failed to read compiled module: Bad file datag++ 11.2.0 读取编译模块失败:文件数据错误
【发布时间】:2021-11-19 14:08:25
【问题描述】:

我为系统头文件一一创建预编译头文件

$ g++ -fmodules-ts -std=c++20 -x c++-system-header concepts
$ g++ -fmodules-ts -std=c++20 -x c++-system-header limits
$ g++ -fmodules-ts -std=c++20 -x c++-system-header iostream

我将所需的系统标头收集到 Reverse 中

$ cat Reverse
import <concepts>;
import <limits>;
import <iostream>;

$  g++ -fmodules-ts -std=c++20 -x c++-header Reverse

$ ls gcm.cache/,
Reverse.gcm

我尝试导入模块头

$ head Reverse.cpp
import <Reverse>;

$ make
g++ -fmodules-ts -std=c++20 -I./  -o Reverseexec Reverse.cpp
In module imported at Reverse.cpp:4:1:
./Reverse: error: failed to read compiled module: Bad file data
./Reverse: note: compiled module file is ‘gcm.cache/,/Reverse.gcm’
./Reverse: fatal error: returning to the gate for a mechanical issue
compilation terminated.
make: *** [Makefile:2: Reverseexec] Error 1

我更改了 Reverse.cpp 中的导入并编译

$ head Reverse.cpp
import <concepts>;
import <limits>;
import <iostream>;

$ make
g++ -fmodules-ts -std=c++20 -o Reverse Reverse.cpp

我发现了一个似乎相关的错误。

[Bug c++/98944] [modules] 无法读取未导出分区的已编译模块。

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/11/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: /mnt/share/cygpkgs/gcc/gcc.x86_64/src/gcc- 
11.2.0/configure --srcdir=/mnt/share/cygpkgs/gcc/gcc.x86_64/src/gcc-11.2.0 
--prefix=/usr --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc -- 
docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C -- 
build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin - 
-without-libiconv-prefix --without-libintl-prefix --libexecdir=/usr/lib -- 
with-gcc-major-version-only --enable-shared --enable-shared-libgcc -- 
enable-static --enable-version-specific-runtime-libs --enable-bootstrap -- 
enable-__cxa_atexit --with-dwarf2 --with-tune=generic --disable-bootstrap 
--enable-languages=c,c++,fortran,lto,objc,obj-c++,jit --enable-graphite -- 
enable-threads=posix --enable-libatomic --enable-libgomp --enable- 
libquadmath --enable-libquadmath-support --disable-libssp --enable-libada 
--disable-symvers --with-gnu-ld --with-gnu-as --with-cloog- 
include=/usr/include/cloog-isl --without-libiconv-prefix --without- 
libintl-prefix --with-system-zlib --enable-linker-build-id --with-default- 
libstdcxx-abi=gcc4-compatible --enable-libstdcxx-filesystem-ts
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.2.0 (GCC)

我做错了什么?为什么我会收到错误消息,

g++ -fmodules-ts -std=c++20 -I./  -o Reverseexec Reverse.cpp
In module imported at Reverse.cpp:4:1:
./Reverse: error: failed to read compiled module: Bad file data
./Reverse: note: compiled module file is ‘gcm.cache/,/Reverse.gcm’
./Reverse: fatal error: returning to the gate for a mechanical issue
compilation terminated.
make: *** [Makefile:2: Reverseexec] Error 1

【问题讨论】:

  • 模块的 GCC 实现仍然有很多错误。有一个与将标准标题作为标题单元导入有关的问题的元错误:gcc.gnu.org/bugzilla/show_bug.cgi?id=99227。遗憾的是,GCC 的模块开发目前处于中断状态。如果您的错误与元错误中列出的不同,请报告您的错误。

标签: c++ gcc g++ c++20


【解决方案1】:

我想您正在尝试将几个标准标头收集到一个可导入的实体中。 (一开始我没有意识到这一点,还以为你遇到了错误)。

为什么不创建一个合适的模块而不是一个标头单元?

这是我尝试过的。首先,像以前一样编译标题单元:

$ g++ -fmodules-ts -std=c++20 -x c++-system-header concepts
$ g++ -fmodules-ts -std=c++20 -x c++-system-header limits
$ g++ -fmodules-ts -std=c++20 -x c++-system-header iostream

然后,创建一个导出所有标题的模块:

export module Reverse;

export import <concepts>;
export import <limits>;
export import <iostream>;

并编译它:

$ g++ -fmodules-ts -std=c++20 -c Reverse.cpp

并最终在程序中使用它(Program.cpp):

import Reverse;

int main()
{
  static_assert(std::integral<decltype(std::numeric_limits<int>::max())>);
  std::cout << "Hello world\n";
  return 0;
}

编译:

$  g++ -fmodules-ts -std=c++20 Program.cpp Reverse.o -o foo

它为我编译并正确运行。

我在 linux-gnu x86_64 下使用 GCC 版本 11.2 对此进行了测试。

【讨论】:

    【解决方案2】:

    这适用于捆绑预编译系统头文件的特殊情况。如果你需要做一些实质性的事情怎么办?例如,

    $ cat Reverse.cpp
    export module Reverse;
    
    export import <concepts>;
    export import <limits>;
    export import <iostream>;
    
    namespace Reverse {
        constexpr int TEST = { 1 };
    }
    
    $ cat main.cpp
    import Reverse;
    
    int
    main(int, char **)
    {
        std::cout << Reverse::TEST << std::endl;
        return 0;
    }
    
    $ g++ -fmodules-ts -std=c++20 main.cpp Reverse.o -o main
        main.cpp: In function ‘int main(int, char**)’:
        main.cpp:6:22: error: ‘Reverse’ has not been declared
        6 |         std::cout << Reverse::TEST << std::endl;
          |                      ^~~~~~~
    

    感谢您的帮助。

    我担心的另一个问题是,让实验模块工作的神奇方式很大程度上取决于平台。这将降低跨平台兼容性,并且可能需要构建系统付出更多努力。我知道模块是为了简化事情,但在 VS 和 g++ 上使用 import 的不同方法可能不容易实现跨平台兼容性。这对我来说是一种耻辱。

    【讨论】:

    • 如果您想对某个答案发表评论,请使用“评论”按钮。如果您想澄清问题,请编辑它而不是写答案。你不能使用TEST,因为你还没有exported它。
    • 感谢您的建议。是的,我需要将其导出并内联。我可能会为使用 constexpr 内联的要求创建一个错误。
    • 我不确定这是一个错误。在旧的标头包含模型中,如果变量被 ODR 使用,您还必须 inline constexpr 变量。我不知道无论模块是否用于 ODR,该要求是否已得到加强。无论如何,最好将全局变量constexpr 声明为inline constexpr。与static constexpr 成员变量相比,它们不是隐含的inline
    • 好问题,似乎有一些争论。从 en.cppreference.com/w/cpp/language/constexpr 开始,在函数或静态数据成员 (C++17 起) 声明中使用的 constexpr 说明符暗示内联。我在 gcc bugzilla 中创建了一个错误“错误 102545 - [modules] 需要内联 constexpr 但它不应该。”
    • TEST 既不是函数也不是静态数据成员。
    【解决方案3】:

    我得到了或多或少的工作。我很惊讶我仍然需要内联声明所有内容,我认为 constexpr 和 consteval 修饰符可以避免内联。

    为了让事情更简单,创建了 Makefile,

    .PHONY: default
    default: main ;
    
    gcmcache:
            g++ -fmodules-ts -std=c++20 -x c++-system-header iostream
    
    hello.o: hello.cpp
            g++ -c -std=c++20 -fmodules-ts hello.cpp
    
    main: main.cpp hello.o
            g++ -fmodules-ts -std=c++20 -o main main.cpp hello.o
    
    clean:
            rm hello.o main.exe rm -rf gcm.cache
    

    带它去跑步,

    $ make clean gcmcache main
    rm hello.o main.exe rm -rf gcm.cache
    g++ -fmodules-ts -std=c++20 -x c++-system-header iostream
    g++ -c -std=c++20 -fmodules-ts hello.cpp
    g++ -fmodules-ts -std=c++20 -o main main.cpp hello.o
    
    $ cat hello.cpp
    module;
    
    export import <iostream>;
    
    export module hello;
    
    namespace hello {
    
            export inline constexpr const int TEST = { 1 };
    
            export inline consteval int foo( const int testing = TEST )
            {
                    return testing;
            }
    }
    
    $ cat main.cpp
    import hello;
    
    int main ( int, char** )
    {
            std::cout << hello::TEST << std::endl;
            std::cout << hello::foo( hello::TEST ) << std::endl;
            return 0;
    }
    

    除了看似多余的内联之外,一切似乎都可以工作。

    【讨论】:

    • hello.cpp 有一个奇怪的结构。它有一个以module; 开头的全局模块片段,尽管你没有#includeing 任何东西。此外,我认为export import &lt;iostream&gt;;应该放在export module hello;之后
    • 我同意你的看法。我在下一个版本中清理了它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-31
    • 2023-03-05
    • 2012-10-02
    • 2011-07-29
    • 1970-01-01
    • 2018-07-11
    • 2014-06-23
    相关资源
    最近更新 更多