使用CRTP分离特定于平台的代码

时间:2016-08-24 13:14:05

标签: c++ crtp

我最近有了这个想法,使用CRTP(奇怪的重复模板模式)分离不同的平台特定实现(可能是Win32 / X,opengl / dx / vulkan等等):我想到这样的事情:

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等等),指针调用很重要,我避免使用虚构和多态。

2 个答案:

答案 0 :(得分: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();

答案 1 :(得分:2)

我在这里没有真正看到CRTP的好处,你仍然在代码中具有特定于平台(而不是特定于功能)的ifdef,这往往会使事情更难以阅读和维护。我通常更喜欢在不同的源文件中使用不同的实现 - 事实上,通常每个平台都有单独的目录。

如:

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

通过这种方式,您可以在很大程度上避免ifdef混乱,并且您通常知道在哪里可以找到特定于平台的内容。您还知道需要编写什么才能将内容移植到另一个平台。然后可以使构建系统选择正确的源而不是预处理器。