C++ ';内联';用于_全局_函数,以避免多定义错误

C++ ';内联';用于_全局_函数,以避免多定义错误,c++,cuda,linker,C++,Cuda,Linker,我有一个CUDA模板库,其中一个函数实际上不是模板,而是在.cuh头中定义的。(vector\u在下面的kernel.cuh中添加内核) 如果多个.cu文件包含kernel.cuh并调用vector\u add[\u kernel],则会在链接时导致多个定义错误。在C++中,可以使用内联限定符来避免此类错误。 但是,inline\uuuu global\uuuu…-虽然防止了我的系统上的多个定义错误,但会导致一个警告,即inline限定符已被忽略 Q:是否有更好的方法避免多重定义错误,或者有没有

我有一个CUDA模板库,其中一个函数实际上不是模板,而是在
.cuh
头中定义的。(
vector\u在下面的
kernel.cuh
中添加内核

如果多个
.cu
文件包含
kernel.cuh
并调用
vector\u add[\u kernel]
,则会在链接时导致多个定义错误。在C++中,可以使用<代码>内联限定符来避免此类错误。

但是,
inline\uuuu global\uuuu…
-虽然防止了我的系统上的多个定义错误,但会导致一个警告,即
inline
限定符已被忽略

Q:是否有更好的方法避免多重定义错误,或者有没有一种方法仅针对此函数抑制此警告?
inline\uuuuu global\uuuuuu
是否安全,或者其他主机编译器是否真的会忽略它

我可以简单地将
vector\u add\u kernel
移动到一个单独的
.cu
文件中,但它将是唯一的非头文件。我也可以用模板
向量添加内核
,但在我的库中这没有什么意义

下面是一个(不是很简单,很抱歉)工作示例(在Debian上使用CUDA 7.0、gcc 4.7.2进行测试)

澄清一下,
main.cu
是一些用户的代码
lib.cu
是一些不属于我的外部库;而且
kernel.cuh
是我的模板库的一部分。因此,外部
lib
和用户的
main
都在使用我的模板库
kernel.cuh
——但它们是分开的

main.cu

#include "lib.hpp"
#include "kernel.cuh"

#include <thrust/device_vector.h>
#include <thrust/host_vector.h>

#include <cstddef>
#include <cstdlib>
#include <iostream>

int main(void)
{
    const size_t N = 1u << 7;

    float* a = (float*) malloc(N * sizeof(float));
    float* b = (float*) malloc(N * sizeof(float));
    float* c = (float*) malloc(N * sizeof(float));

    for (int i = 0; i < N; ++i) {
        a[i] = b[i] = 2.0f * i;
    }

    lib_vector_add(a, b, c, N);
    for (int i = 0; i < N; ++i) {
        if (c[i] != 2.0f * i + 2.0f * i)
            std::cout << "Error, lib, element " << i << std::endl;
    }

    thrust::device_vector<float> d_a(a, a + N);
    thrust::device_vector<float> d_b(b, b + N);
    thrust::device_vector<float> d_c(N);

    vector_add(d_a, d_b, d_c);
    thrust::host_vector<float> h_c = d_c;
    for (int i = 0; i < N; ++i) {
        if (h_c[i] != 2.0f * i + 2.0f * i)
            std::cout << "Error, element " << i << std::endl;
    }
}
lib.hpp

#pragma once

#include <cstddef>

void lib_vector_add(float*, float*, float*, size_t);
Makefile

OBJS = main.o lib.o
DEPS = kernel.cuh
CU_ARCH = -gencode arch=compute_20,code=sm_20

all: app

app: $(OBJS)
    nvcc $(CU_ARCH) $(OBJS) -o app

%.o: %.cu $(DEPS)
    nvcc $(CU_ARCH) -dc -I./ $< -o $@

clean:
    -rm *.o
OBJS=main.o lib.o
DEPS=kernel.cuh
CU_ARCH=-gencode ARCH=compute_20,code=sm_20
全部:应用程序
应用程序:$(OBJS)
nvcc$(CU_ARCH)$(OBJS)-o应用程序
%.o:%.cu$(DEPS)
nvcc$(CU_拱门)-dc-I./$<-o$@
清洁:
-rm*.o

如果您想保持当前的代码组织,您有一个非常简单的解决方案,那就是声明内核
静态
(代替
内联
关键字)。这将防止链接器抱怨,但是会生成与包含
kernel.cuh
的编译单元(对象文件)一样多的内核版本


另一个解决方案是将内核模板化。我知道您已经排除了这种可能性,但您应该重新考虑,因为您的内核是输入参数类型的自然模板。

更好的方法是从头文件中获取函数定义。这是一般性的建议,不是CUDA独有的。您已经有了
lib.cu
,为什么不把它放在那里呢?因为实际上,只有
kernel.cuh
是“我的”代码
lib.cu
是一些外部库,它使用我的
kernel.cuh
,而
main.cu
是一些未知用户的代码,他们同时使用我的
kernel.cuh
和外部
lib
。也许在内核定义周围使用
\ifdef\ucuda\uarch\ucode>会有所帮助?这样,它只有在由nvcc处理时才会被编译。@void\u ptr这是无效的
\uuuuu CUDA\u ARCH\uuuuu
用法(除了下面答案中合适的解决方案外)可能有效的方法是将函数定义包装在
\ifdef FOO#endif
kernel.cuh
中,然后记录它,以便用户(编写
main.cu
的人)在执行
之前在
main.cu
中定义FOO
。那么
FOO
将不会在
lib.cu
中定义,因此该函数不会被定义两次。
#pragma once

#include <thrust/device_vector.h>
#include <cstddef>

// inline keyword avoids multiple definition errors, but produces warnings.
// UNCOMMENT TO GET A WORKING EXECUTABLE.
// inline
__global__ void vector_add_kernel(
    const float *const a,
    const float *const b,
    float *const c,
    const size_t N)
{
    int tid = threadIdx.x + blockIdx.x * blockDim.x;

    while (tid < N)
    {
        c[tid] = a[tid] + b[tid];
        tid += blockDim.x * gridDim.x;
    }
}

// inline produces no warnings.
inline
void vector_add(
    const thrust::device_vector<float>& d_a,
    const thrust::device_vector<float>& d_b,
    thrust::device_vector<float>& d_c)
{
    const float *const a_ptr = thrust::raw_pointer_cast(d_a.data());
    const float *const b_ptr = thrust::raw_pointer_cast(d_b.data());
    float *const c_ptr = thrust::raw_pointer_cast(d_c.data());

    const size_t N = d_a.size();

    dim3 block(128);
    dim3 grid((N + 127) / 128);

    vector_add_kernel<<<grid, block>>>(a_ptr, b_ptr, c_ptr, N);
}
OBJS = main.o lib.o
DEPS = kernel.cuh
CU_ARCH = -gencode arch=compute_20,code=sm_20

all: app

app: $(OBJS)
    nvcc $(CU_ARCH) $(OBJS) -o app

%.o: %.cu $(DEPS)
    nvcc $(CU_ARCH) -dc -I./ $< -o $@

clean:
    -rm *.o