2017-08-09 57 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

相關問題