2016-08-24 14 views
4

Недавно я получил эту идею для разделения различных реализаций на платформе (может быть Win32/X, opengl/dx/vulkan и т. Д.) С использованием CRTP (любопытно повторяющийся шаблон шаблона): я думал что-то вроде этого:Использование 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 и т. Д.), Где указатель вызывает материю.

+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

sry для глупого вопроса; но платформа определяется во время компиляции; вы бы набрали DisplayDevice? – Coder32

2

Я действительно не вижу преимущества CRTP здесь, у вас все еще есть специфичная для платформы (в отличие от особенности) ifdefs внутри кода, и это, как правило, усложняет чтение и обслуживание. Обычно я предпочитаю иметь разные реализации в разных исходных файлах - и на самом деле, как правило, отдельные каталоги для каждой платформы.

, такие как:

  • платформы/Win64
  • платформы/win32
  • платформы/гну-линукс
  • Платформа/FreeBSD

Таким образом, вы можете в значительной степени избежать ifdef clutter, и вы обычно знаете, где найти конкретные вещи платформы. Вы также знаете, что вам нужно написать, чтобы переносить вещи на другую платформу. Затем можно построить систему сборки, чтобы выбрать правильный источник, а не препроцессор.

+0

спасибо за предложение :) – Coder32

+0

btw разными вариантами вы имеете в виду один .h и несколько .cpp-s? – Coder32

+0

и о системе сборки; вы думаете, что это можно сделать в cmake? – Coder32