C++ 链接顺序规定在另一个模板类中使用一个类的哪个版本,如何控制使用哪个版本?

C++ 链接顺序规定在另一个模板类中使用一个类的哪个版本,如何控制使用哪个版本?,c++,cuda,linker,C++,Cuda,Linker,我正在研究一些跨平台的代码,某些顶级抽象包含CPU和GPU的相同代码,而它们调用的较低级别代码可以通过选中\uuuuuu CUDACC\uuuuu标志为任一设备构建,该标志被认为是由NVCC编译器为其涉及的所有内容定义的顶级代码 不幸的是,看起来应该用NVCC编译的某些头文件中的代码,不管出于什么原因,都被编译为普通CPU代码,而我需要将其编译为CUDA代码 这个最小的例子更好地解释了我的意思。zip存档中的所有文件,包括CMake,都可以从下载 main.cpp #include "Class

我正在研究一些跨平台的代码,某些顶级抽象包含CPU和GPU的相同代码,而它们调用的较低级别代码可以通过选中
\uuuuuu CUDACC\uuuuu
标志为任一设备构建,该标志被认为是由NVCC编译器为其涉及的所有内容定义的顶级代码

不幸的是,看起来应该用NVCC编译的某些头文件中的代码,不管出于什么原因,都被编译为普通CPU代码,而我需要将其编译为CUDA代码

这个最小的例子更好地解释了我的意思。zip存档中的所有文件,包括CMake,都可以从下载

main.cpp

#include "ClassA.hpp"

int main() {
    ClassA<DEVICE_CPU> a_cpu_instance;
    a_cpu_instance.PrintDevice();
    a_cpu_instance.PrintClassBDevice();
    ClassA<DEVICE_CUDA> a_cuda_instance;
    a_cuda_instance.PrintDevice();
    a_cuda_instance.PrintClassBDevice();
    return 0;
}
#pragma once

#include "Device.hpp"

template <Device device>
class ClassA{
public:
    void PrintDevice();
    void PrintClassBDevice();
};
#pragma once
#include "ClassA.hpp"
#include "ClassB.hpp"

template<Device device>
void ClassA<device>::PrintDevice() {
    {
#if defined(__CUDACC__)
        printf("CUDA\n");
#else
        printf("CPU\n");
#endif
    }
}

template<Device device>
void ClassA<device>::PrintClassBDevice() {
    ClassB b_instance;
    b_instance.PrintDevice();
}
#pragma once

#include <cstdio>
class ClassB{
public:
    void PrintDevice(){
#if defined(__CUDACC__)
        printf("CUDA\n");
#else
        printf("CPU\n");
#endif
    }

};
A类水电站

#include "ClassA.hpp"

int main() {
    ClassA<DEVICE_CPU> a_cpu_instance;
    a_cpu_instance.PrintDevice();
    a_cpu_instance.PrintClassBDevice();
    ClassA<DEVICE_CUDA> a_cuda_instance;
    a_cuda_instance.PrintDevice();
    a_cuda_instance.PrintClassBDevice();
    return 0;
}
#pragma once

#include "Device.hpp"

template <Device device>
class ClassA{
public:
    void PrintDevice();
    void PrintClassBDevice();
};
#pragma once
#include "ClassA.hpp"
#include "ClassB.hpp"

template<Device device>
void ClassA<device>::PrintDevice() {
    {
#if defined(__CUDACC__)
        printf("CUDA\n");
#else
        printf("CPU\n");
#endif
    }
}

template<Device device>
void ClassA<device>::PrintClassBDevice() {
    ClassB b_instance;
    b_instance.PrintDevice();
}
#pragma once

#include <cstdio>
class ClassB{
public:
    void PrintDevice(){
#if defined(__CUDACC__)
        printf("CUDA\n");
#else
        printf("CPU\n");
#endif
    }

};
除了最后一行,一切都好。我需要为CUDA编译单元中的ClassB头定义
\uuuuu CUDACC\uuuu
(按照.cu文件的指导),但事实并非如此。(是的,但是使用了错误的版本,请参见编辑和回答)另外,假设
\uuuuudacc\uuuuuuuu
指导
ClassB
的实际定义,而不仅仅是
PrintDevice()
的实现,我希望它在同一个单元中编译,因此,我无法在与
ClassA
单元不同的单元中实例化两个不同版本的
ClassB

我该怎么做

另外,请随意降低所提供文件中所需的CMake版本,我认为它应该适用于>=3.9的任何内容

[编辑]2条新信息

  • 显然,我指定要制作的文件的顺序,即
    ClassA\u CUDA.cu ClassA\u CPU.cpp
    ClassA\u CPU.cpp ClassA\u CUDA.cu
    决定了在main.cpp中使用哪个版本的ClassB。我认为这并没有什么神奇之处,而是链接器的参数顺序,对应于这两个单元中编译的对象文件。作为参考,我使用的是标准的GCC链接器(
    ld

  • 我知道ClassB的两个版本都会被编译,因为当我在ClassB中的两个不同的预处理器分支中放置
    #warning COMPILING CUDA VERSION
    这类警告时,我会得到两个输出(“CUDA”两次,出于某种原因)


  • [P>] [旁注:这个问题似乎是“强”>关于C/C++代码与不同定义的链接。CUDA用户可能更频繁地遇到这个问题,但实际上,如果只编译一个带有“-D”标志的C++单元,而另一个没有,不使用CUDA,则会得到同样的效果。问题似乎在于链接器对于类
    ClassB
    中的事物基本上有两组等价的符号,因此默认情况下它将使用第一组符号,具体取决于链接器的顺序

    解决方案很简单(虽然不是很明显,至少对我来说)就是在
    设备上设置
    ClassB
    ,即

    #pragma once
    
    #include <cstdio>
    
    template <Device>
    class ClassB{
    public:
        void PrintDevice(){
    #if defined(__CUDACC__)
            printf("CUDA\n");
    #else
            printf("CPU\n");
    #endif
        }
    
    };
    
    #pragma一次
    #包括
    模板
    B类{
    公众:
    无效打印设备(){
    #如果已定义(uuu CUDACC_uuuuu)
    printf(“CUDA\n”);
    #否则
    printf(“CPU\n”);
    #恩迪夫
    }
    };
    
    显然,当在ClassA中使用时,这需要使用模板参数来修饰它,如下所示:

    template<Device device>
    void ClassA<device>::PrintClassBDevice() {
        ClassB<device> b_instance;
        b_instance.PrintDevice();
    }
    
    模板
    void ClassA::PrintClassB设备(){
    b类b_实例;
    b_instance.PrintDevice();
    }
    

    <>这将导致编译器产生两个不同的符号集,在链接

    时可以正确地区分它们。可以尝试<代码> NVCC -X Cu</代码>强制将C++文件编译为CUDA文件。或者将类B的主体放在一个.cu文件中。@JHBonarius我马上就会看到,但神秘的是:当我将
    #warning compileding CUDA VERSION
    放在
    下,如果在类B中定义了(u CUDACC)
    ,它实际上会在编译过程中打印出警告。。。两次。。。我很困惑。@JHBonarius the
    -xcu
    在默认情况下已经打开了,显然,CMakeThis的优点在某些情况下仍然会产生意想不到的效果。如果在.cu文件中编译
    DEVICE\u CPU
    实例,您将获得“CUDA”行为,这可能不是您想要的。一旦你真的有GPU代码要编译,事情可能也不会像你预期的那样工作。是的,诀窍是跟踪,而不是在.cu内编译设备CPU代码,你可以通过显式实例化完全控制这一点。GPU代码(也有很多)已经按预期工作了,所以我对这个解决方案很满意。