1 问题及解决
在写代码的时候发现了一个问题:我们知道,在一个工程中可能存在多个实现文件include同一个头文件,然后在最后链接的时候导致重声明错误,因此,我们一般使用编译器指令#pragma once
或者#ifndef
来解决该问题,但是仍然存在一种场景,是如上两种方法解决不了的。
实现文件的include层次如下:
然后,在utils.cuh
中,我直接定义了一个handlError
函数【注意,是定义,不是声明】:
void handleError(cudaError_t err, const char *file, const int line){
if (err != cudaSuccess){
printf("file: %s, line: %d: There is a error happening!\n", file, line);
printf("The error reason is: %s\n", cudaGetErrorString(err));
exit(-1);
}
}
于是,在我进行编译的时候,就会出现:
/tmp/tmpxft_000075d6_00000000-21_storage.o: In function `handleError(cudaError, char const*, int)':
tmpxft_000075d6_00000000-9_storage.cudafe1.cpp:(.text+0x267): multiple definition of `handleError(cudaError, char const*, int)'
/tmp/tmpxft_000075d6_00000000-18_blas_test.o:tmpxft_000075d6_00000000-5_blas_test.cudafe1.cpp:(.text+0x267): first defined here
/tmp/tmpxft_000075d6_00000000-24_blas.o: In function `handleError(cudaError, char const*, int)':
tmpxft_000075d6_00000000-13_blas.cudafe1.cpp:(.text+0x267): multiple definition of `handleError(cudaError, char const*, int)'
/tmp/tmpxft_000075d6_00000000-18_blas_test.o:tmpxft_000075d6_00000000-5_blas_test.cudafe1.cpp:(.text+0x267): first defined here
collect2: error: ld returned 1 exit status
可知该错误就是多重定义错误。这是怎么回事呢?看一下include层次图,在blas.cuh
中出现了分支,但是分支都回到了utils.cuh
上,于是,我们的handleError
函数就被定义了两次,这肯定是不允许的。那这个问题怎么解决呢?有如下解决方法:
-
第一种,我们一般情况下都不会在头文件中对函数进行定义,不然就可能会导致这个问题产生【这里是我写代码时犯的一个错误】。于是,我们选择头文件声明,对应的实现文件定义来解决这个问题。
-
第二种,在该函数前面加
inline
声明:inline void handleError(cudaError_t err, const char *file, const int line){ if (err != cudaSuccess){ printf("file: %s, line: %d: There is a error happening!\n", file, line); printf("The error reason is: %s\n", cudaGetErrorString(err)); exit(-1); } }
为什么加inline有用呢?因为在头文件中放入了一个inline函数的定义,就保证了每个inline函数都只有一个定义。所以便可解决多重定义问题。并且,我们一般都在头文件中对inline函数进行声明,然后需要使用该inline函数的话,就include该头文件即可。最后补充一点,inline必须加在函数定义体前面才有效,加在函数声明前面是无效的。
2 总结
所以,为了避免多重定义问题,我们应该规范好自己的代码写作,保证:
- 尽量让头文件声明,对应的实现文件进行定义;
- 将inline函数的定义放在头文件中。