GCC Compile Error

导言

遇到过的c++编译error

大型项目(许多git未跟踪的三方库+临时文件)重新编译报错

  • 详细记录从干净git仓库编译的流程
  • 及时git保存代码的修改
  • 使用git clean -dfx来回退。其余make cleanpython setup.py clean都不靠谱,毕竟具体操作也是人写的,很可能过时,未维护了。

git clean -dfx注意事项

  • 命令是删除多余的文件和目录(包括.gitignore里),对于已跟踪文件里的修改不会还原,但是新文件和目录会被删除。
  • 建议先git stash或者git stage保存。
选项说明:

* `-d` 选项表示同时删除未跟踪的目录。
* `-f` 选项表示强制执行,即使未跟踪的文件和目录与已跟踪的文件同名也会被删除。
* `-x` 选项表示删除所有未跟踪的文件(包括那些**在`.gitignore`文件中指定忽略的文件**)。

libxxx.so

1
ImportError: /home/anaconda3/envs/t00906153_bindCore/lib/python3.8/site-packages/torch_npu/lib/libtorch_npu.so: undefined symbol: _ZN7c10_npu17SetThreadAffinityEi
  • c++filt _ZNK4Json5ValueixEPKc得到unwind的函数名。
  • nm -gD libxxx.so |grep name 确定 是否未定义
  • 编译环境的不同会导致,编译选项多了-D_GLIBCXX_USE_CXX11_ABI=1,会让函数在wind的过程中的结尾有类似__cxx1112basic_,这细微的差别会导致匹配不上。
  • fzf查看编译中间变量.o是否生成
    • 如果没有,很可能是make时没有添加cpp, 或者cmake没有添加新文件

Error

1
2
3
4
CMakeFiles/torch_npu.dir/torch_npu/csrc/utils/TensorType.cpp.o:(.bss+0x58): multiple definition of c10_npu::threadNameToType'
CMakeFiles/torch_npu.dir/torch_npu/csrc/aten/NPUGeneratorImpl.cpp.o:(.bss+0xb0): first defined here
CMakeFiles/torch_npu.dir/torch_npu/csrc/utils/TensorType.cpp.o:(.bss+0x28): multiple definition of c10_npu::threadNames'
CMakeFiles/torch_npu.dir/torch_npu/csrc/aten/NPUGeneratorImpl.cpp.o:(.bss+0x80): first defined here

这种错误通常发生在全局变量在多个源文件中定义了不止一次。即使你使用了 extern 关键字,在多个源文件中定义全局变量时仍然会产生“multiple definition”错误。extern 声明只是告诉编译器这个变量在别的地方定义,而不是在当前文件中定义。

要解决这个问题,你需要确保全局变量的定义仅存在于一个源文件中。以下是详细的步骤:

  1. 在头文件中声明全局变量

在头文件中使用 extern 声明全局变量,而不定义它们。确保在所有需要引用这些变量的源文件中都包含这个头文件,但不要在头文件中定义它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ThreadUtils.h
#pragma once
#include <map>
#include <string>

namespace c10_npu {

enum ThreadType {
Release_thread = 1,
ACL_thread = 2,
Forward_thread = 3,
Backward_thread = 3
};

// 使用 extern 声明全局变量
extern const std::map<ThreadType, std::string> threadNames;
extern const std::map<std::string, ThreadType> threadNameToType;

} // namespace c10_npu
  1. 在源文件中定义全局变量

在一个源文件中定义这些全局变量。确保这个源文件中只包含变量的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ThreadUtils.cpp
#include "ThreadUtils.h"

namespace c10_npu {

// 在一个源文件中定义全局变量
const std::map<ThreadType, std::string> threadNames = {
{Release_thread, "Release_thread"},
{ACL_thread, "ACL_thread"},
{Forward_thread, "Other_thread"},
{Backward_thread, "Other_thread"}
};

const std::map<std::string, ThreadType> threadNameToType = {
{"Release_thread", Release_thread},
{"ACL_thread", ACL_thread},
{"Other_thread", Forward_thread} // Forward_thread 和 Backward_thread 映射为 "Other_thread"
};

} // namespace c10_npu
  1. 确保每个源文件只引用头文件,不重复定义变量

确保在其他源文件中(如 NPUGeneratorImpl.cppTensorType.cpp)只包含头文件 ThreadUtils.h,而不是在这些源文件中定义全局变量。这可以避免在多个编译单元中重复定义相同的全局变量。

  1. 检查和清理项目

如果你仍然遇到问题,检查项目中是否有重复的定义,可能是某些源文件中无意间重复了全局变量的定义。确保清理旧的编译文件(如使用 make cleanrm -rf build),然后重新编译项目,以确保没有残留的旧文件导致冲突。

总结

  • 使用 extern 在头文件中声明全局变量。
  • 在一个源文件中定义这些全局变量。
  • 确保其他源文件只包含头文件而不重复定义全局变量。
  • 清理项目并重新编译。

这些步骤将帮助你解决“multiple definition”错误。

error

1
2
3
4
/root/document/shaojie/github/v2.1.0-6.0.rc2/torch_npu/csrc/core/npu/NPUQueue.cpp:517:63: error: passing ‘const std::map<c10_npu::ThreadType, std::basic_string<char> >’ as ‘this’ argument discards qualifiers [-fper
missive]
if (prctl(PR_SET_NAME, (threadNames[ThreadType::ACL_thread])) != 0) {
^

这个错误是由于你尝试将一个const修饰的std::map对象的元素传递给需要修改该对象的方法。具体来说,std::map::operator[]返回的引用是可修改的,但是你试图在const std::map对象上调用它,这导致了错误。

解决方案
如果你不需要修改threadNames这个std::map,而只是需要访问其元素,可以使用std::map::at()方法代替operator[]at()方法是一个const方法,可以在const std::map上安全地调用。

以下是修正后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "ThreadUtils.h"

namespace c10_npu {

const std::map<ThreadType, std::string> threadNames = {
{Release_thread, "Release_thread"},
{ACL_thread, "ACL_thread"},
{Forward_thread, "Other_thread"},
{Backward_thread, "Other_thread"}
};

const std::map<std::string, ThreadType> threadNameToType = {
{"Release_thread", Release_thread},
{"ACL_thread", ACL_thread},
{"Other_thread", Forward_thread} // Forward_thread 和 Backward_thread 映射为 "Other_thread"
};

// 在代码中,使用at()方法访问map的值
void setThreadName() {
if (prctl(PR_SET_NAME, threadNames.at(ThreadType::ACL_thread).c_str()) != 0) {
// 错误处理代码
}
}

} // namespace c10_npu

关键点

  1. at()方法std::map::at()方法与operator[]类似,但它是const安全的,可以在const std::map对象上使用。它还会在键不存在时抛出std::out_of_range异常,而不是插入新元素。

  2. 避免修改操作:使用const修饰std::map的对象表示你不希望它的内容被修改,因此应避免使用会改变其内容的操作,如operator[]

通过这种方式,你可以在不改变threadNames的情况下访问其中的元素,并解决const修饰符导致的编译错误。

参考文献

Author

Shaojie Tan

Posted on

2024-08-22

Updated on

2025-01-30

Licensed under