2016-08-24 80 views
4

我最近得到了這個想法,使用CRTP(奇怪的循環模板模式)分離不同平臺的具體實現(可能是Win32/X,opengl/dx/vulkan等):我想大概是這樣:使用CRTP分離平臺特定的代碼

IDisplayDevice.h

#pragma once 
#include "OSConfig.h" 

namespace cbn 
{ 

    template <class TDerived> // Win32 type here 
    struct IDisplayDevice 
    { 
     bool run_frame(void) { 
      return 
       static_cast<const TDerived*>(this)->run_frame(); 
     } 
     // a lot of other methods ...  
    }; 
} 

Win32DisplayDevice.h

#pragma once 
#include "OSConfig.h" 
// make sure it only gets compiled on win32/64 
#if defined(CBN_OS_WINDOWS) 

namespace cbn 
{ 
    class CWin32DisplayDevice 
     : public IDisplayDevice<CWin32DisplayDevice> { 
    public: 
     bool run_frame(void) { 
      call_hInstance(); 
      call_hWnd(); 
      #ifdef CBN_RENDERAPI_DX11 
      call_dx11_bufferswap(); 
      #endif 
      return some_state; 
     } 
    private: 
    }; 
} 
#endif 

然後,我會在XDisplayDevice.h中以相同的方式提供其他實現。 最後,我會做一個公共接口DisplayDevice.h

#include "Win32DisplayDevice.h" 
#include "XDisplayDevice.h" 

namespace cbn 
{ 
    class CDisplayDevice 
    { 
    public: 
     CBN_INLINE 
     bool run_frame(void) { return device_->run_frame(); } 
    private: 
#if defined(CBN_OS_WINDOWS) 
     CWin32DisplayDevice device_; 
#elif defined(CBN_OS_LINUX) 
     CXDisplayDevice device_; 
#elif // and so on 
#else 
     // does nothing ... 
     CNillDisplayDevice device_; 
#endif 
    } 
} 

所以,我可以把它在main.cpp中,如:

int main() 
{ 
    CDisplayDevice my_device; 
    while(my_device->run_frame()) 
    { 
     do_some_magic(); 
    } 
} 

你認爲這將是一個處理平臺特定代碼的好方法? PS:由於平臺約束(android,ps4等),我避免了victuals和多態,其中指針調用很重要。

+1

爲了使用CRTP的,你需要從'IDisplayDevice派生'。 –

+2

他們有多重要_do_(指針通過vtables調用)?你有具體的限制或要求嗎? – utnapistim

+0

oops;抱歉;我剛纔寫了這段代碼(不是複製粘貼):) – Coder32

回答

3

考慮以下代碼:

struct OpenGLTraits // keep this in it's own files (.h and .cpp) 
{ 
    bool run_frame() { /* open gl specific stuff here */ } 
}; 


struct VulkanTraits // keep this in it's own files (.h and .cpp) 
{ 
    bool run_frame() { /* vulkan specific stuff here */ } 
}; 

template<typename T> 
class DisplayDevice 
{ 
    using graphic_traits = T; 
    graphic_traits graphics; // maybe inject this in constructor? 

    void do_your_operation() 
    { 
     if(!graphics.run_frame()) // subsystem-specific call 
     { ... } 
    } 
}; 

這將使用特定的子系統通話,和抽象他們離開一個通用的API之間,不涉及虛擬呼叫。您甚至可以內嵌run_frame()實現。

編輯(地址評論問題):

考慮這個問題:

#ifdef FLAG_SPECIFYING_OPEN_GL 
using Device = DisplayDevice<OpenGLTraits>; 
#elif FLAG_SPECIFYING_VULKAN 
using Device = DisplayDevice<VulkanTraits>; 
... 
#endif 

客戶端代碼:

Device device; 
device.do_your_operation(); 
+0

thx發佈的例子:) – Coder32

+0

我真的很喜歡這樣做的特質方式;真的很優雅; – Coder32

+0

由於愚蠢的問題;但平臺是在編譯時決定的;你會鍵入DisplayDevice? – Coder32

2

這裏我沒有真正看到CRTP的好處,您仍然在代碼中具有平臺特定的(與特定功能相對的)ifdefs,而這往往會使事情難以閱讀和維護。我通常更喜歡在不同的源文件中使用不同的實現 - 事實上,通常每個平臺都有獨立的目錄。

如:

  • 平臺/ Win64的
  • 平臺/ win32的
  • 平臺/ GNU Linux的
  • 平臺/ FreeBSD的

這樣可以很大程度上避免ifdef混亂,並且你通常知道在哪裏找到平臺特定的東西。你也知道你需要寫什麼才能將事物移植到另一個平臺。構建系統然後可以選擇正確的源而不是預處理器。

+0

感謝您的建議:) – Coder32

+0

btw通過不同的實現你的意思是一個.h和多個.cpp-s? – Coder32

+0

和關於構建系統;你認爲這可以在cmake中完成嗎? – Coder32