Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using CRTP to separate platform specific code

Tags:

c++

crtp

I recently got this idea to separate different platform specific implementations (could be Win32/X, opengl/dx/vulkan, etc...) using CRTP (curiously recurring template pattern): I thought of something like this:

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

I would then provide an other implementation the same way in XDisplayDevice.h. Finally, I would make a common interface in 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
    }
}

So I could call it in main.cpp like:

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

Do you think this would be a good way to deal with platform specific code ?

PS: I avoid victuals and polymorphism because of platform restraints (android, ps4, etc...) where pointer calls matter.

like image 446
Coder32 Avatar asked Feb 27 '26 17:02

Coder32


1 Answers

Consider this code:

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
        { ... }
    }
};

This will use subsystem-specific calls, and abstract them away between a common API, without a virtual call involved. You can even inline the run_frame() implementations.

Edit (address comment question):

consider this:

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

client code:

Device device;
device.do_your_operation();
like image 127
utnapistim Avatar answered Mar 01 '26 08:03

utnapistim



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!