2017-08-09 26 views
1

目前我正在编写一个类,该类支持使用预处理器定义的cpugpu上的数据处理,以确定要包含哪个header文件。编译同一类的两种不同实现

IE

#ifdef CPU_work 
#include "cpu_backend.h" 
#endif 

#ifdef GPU_work 
#include "gpu_backend.h" 
#endif 

class Work { 
//Implementation dependant upon included header 
} 

然而,也许情况下,我需要这两个变种。反正我有可以做类似....

namespace CPU { 
    #define CPU_work 
    //Generate implementation of WorkClass with cpu_backend.h 
} 
namespace GPU { 
     #define GPU_work 
     //Generate implementation of WorkClass with gpu_backend.h 
} 

和为此决定我通过类似想要的实现......

CPU::Work cpuObject; 
GPU::Work gpuObject; 

会很乐意与任何变通办法也。非常感谢JJ。

+0

**我假设我将有无数人对继承发表评论。然而,这两个不同版本的代码除了调用gpu/cpu的单行方法之外,其他代码都是相同的,尽管只需创建两个独立的子类即可,我不想花时间复制/粘贴30多页代码。 –

+2

这实际上是单个接口“backend.h”的好例子,其中所有声明的函数都与单个名称空间和两个cpp文件“cpu_work.cpp”和“gpu_work.cpp”相同,实现了通用接口他们自己的特殊方式。在构建时链接正确的实现文件。 – user4581301

+0

在C++中这样做的正确方法是继承。打开C++书中的章节,解释如何创建从父类继承的子类,然后开始阅读。您将拥有一个基类和两个实现相应的CPU或GPU特定功能的子类。 –

回答

2

这可能是使用模板方法设计的地方。您的基类实现了CPU和GPU共有的所有内容,然后在有差异的地方使用抽象函数。

class Work { 
public: 
    void execute() { 
     // Do some initializing 
     foo(); 
     // Do some middle stuff 
     bar(); 
     // Do some final stuff 
    } 

private: 
    virtual void foo() = 0; 
    virtual void bar() = 0; 
} 

class CpuWork: public Work { 
    virtual void foo() { 
     // Do some CPU stuff 
    } 
    virtual void bar() { 
     // Do some more CPU stuff 
    } 
} 

class GpuWork: public Work { 
    virtual void foo() { 
     // Do some GPU stuff 
    } 
    virtual void bar() { 
     // Do some more GPU stuff 
    } 
} 

您现在可以不是偶然的,因为它是抽象的使用你的基类Work,你可以不小心调用你的派生类foobar因为它们是基类的私有成员。

0

有趣的问题:)如果我理解你的目标正确,我可以提出一些解决方案。

首先使用模板特化,模板默认参数和(当然)一些宏。

检查了这一点:

// cpu_backend.h 
#define CPU_BACKEND 

class UseCPU; 

#ifndef GPU_BACKEND 
template<class Method = UseCPU> 
struct Backend; 
#endif 

template<> 
struct Backend<UseCPU> 
{ 
    char* Info() { return "CPU"; } 
}; 

// gpu_backend.h 
#define GPU_BACKEND 

class UseGPU; 

#ifndef CPU_BACKEND 
template<class Method = UseGPU> 
struct Backend; 
#endif 

template<> 
struct Backend<UseGPU> 
{ 
    char* Info() { return "GPU"; } 
}; 

// main.cpp 
// Try to swap comments on headers 
// and see how output changes 

#include "cpu_backend.h" 
//#include "gpu_backend.h" 

#include <iostream> 

template<class ... Method> 
struct Work 
{ 
    Work() 
    { 
     std::cout << "I use " << backend.Info() << std::endl; 
    } 

private: 
    Backend<Method ...> backend; 
}; 

int main() 
{ 
    Work<> work; 
    // Uncomment these two while including both headers 
    //Work<UseCPU> cpuWork; 
    //Work<UseGPU> gpuWork; 
    return 0; 
} 

如果使用MSVC可以简化上述消除#define#ifndef例子。

绝招: MSVC(2017年,也许更早版本)允许省略的宏脱粒,只是忽略了第二个声明,如果他们在 满足相同的编译单元,像这样:

template<class Method = UseCPU> 
struct Backend; 
template<class Method = UseGPU> 
struct Backend; 

但是这将不是标准。标准不允许指定默认模板参数两次。

同时,该解决方案有几个缺点:

  • 当包括标题,有人仍然可以说Work<>将 使用指定的后端通过你包括第一位头。 但是,如果编译器在这种情况下强制某人明确指定 后端类型,那么它会更好,因为否则它将依赖于头部包含顺序,这是不好的(例如,向 宏请求问好)。

  • 而且,假定两个后端具有相同的API(如Info() 在我的情况)

对那些可能的解决方法:

  • 我相信这是可能的如果包含 标头并且未指定明确的后端,编译器会报错,但它可能涉及更多预处理器或某些SFINAE ...

  • 如果你的后端是有不同的API,那么你可以插入一些 #ifdef在需要的地方或(最好)使用C++ 17 if constexpr(std::is_same<Method, UseCPU>()::value)如果你有机会 这些很酷的功能:)

+0

绝对是一个很酷的技巧,但我更愿意保持它的标准。看起来像这只会要求错误。 –

+0

对不起,可能我说错了。我提供的代码是标准的(我假设C++ 11)。 Visual Studio中的“诀窍”不是标准的,但它只是让你在没有'#ifndef GPU_BACKEND'和朋友的情况下做同样的事情。如果这就是你所说的“标准”:) – WindyFields

相关问题