如何将奇怪的递归模板模式用于桥接模式

How to use Curiously Recurring Template Pattern for Bridge Pattern?

本文关键字:模式 用于 桥接 递归      更新时间:2023-10-16

我一直在研究Curioly Recurring Template Pattern,以确定如何使用它来实现桥接设计模式。

我的问题是将IBridgeConnector::GetBridgeImpl方法连接到Bridge::Get布里奇Impl方法,因为重写方法实际上是模板化的。

既然虚拟调度在这种情况下不起作用,那么将这些方法相互指向的最佳方式是什么?职能代表?有更好的模式吗?

应该如何做到这一点?

谢谢你的帮助!

在没有shared_ptrs everwhere以及OpenGL和DirectX调用的情况下,尽我所能简化了代码。:)希望这对将来的人有用!

#include <string>
/********************************************************************/
class BridgePart
{
public:
    BridgePart * OtherPart;
};
/********************************************************************/
// Connects a BridgeSource and a BridgeImplementation
class BridgeConnector
{
public:
    static BridgeConnector * implementor;
    // Need a way, (Function Delegates?) to point this method
    // This method will loop until stack overflow.
    template <typename ValueTemplateType>
    BridgePart * GetBridgeImpl(ValueTemplateType * source)
    {
        return implementor->GetBridgeImpl<ValueTemplateType>(source);
    }
};
BridgeConnector * BridgeConnector::implementor = nullptr;
/********************************************************************/
// Where the Magic is At, (CRTP)
template <typename BridgeImplementationTemplateType>
class Bridge : public BridgeConnector
{
public:
    template <typename ValueTemplateType>
    IBridgePart * GetBridgeImpl(IBridgePart * source)
    {
        // NOTE:  This method never gets called.
        // CRTP Magic Here to Semi-Specify Pure Virtual Methods
        return static_cast<BridgeImplementationTemplateType>(this)
            ->GetBridgeImpl( (ValueTemplateType) source);
    }
};
/********************************************************************/
class BridgeImplementation1 : 
    public Bridge<BridgeImplementation1>,
    public BridgePart
{
public:
    class CustomImpl : public BridgePart
    {
    public:
        template <typename SourceTemplateType>
        BridgePart(SourceTemplateType source){}
            /* Does proprietary stuff. */
    };
    template <typename ValueTemplateType>
    BridgePart * GetBridgeImpl(ValueTemplateType & source)
    {
        return new CustomImpl<ValueTemplateType>(source);       
    }
    // Constructor
    BridgeImplementation1()
    {
    }
};
/********************************************************************/
class BridgeSource1 : public BridgePart {};
class BridgeSource2 : public BridgePart {};
class Client
{
    BridgeSource1 source1;
    BridgeSource2 source2;
    BridgeConnector * connector;
    bool usingImpl1;
    Client()
    {
        usingImpl1 = true; // from config file.
        connector = new BridgeConnector();
        connector->implementor = usingImpl1 
            ? (BridgeConnector *) new BridgeImplementation1()
            : nullptr; // (BridgeConnector *) new BridgeImplementation2();  
        // removed to shorten code.
    }
    void Init()
    {
        source1.OtherPart = connector->GetBridgeImpl<BridgeSource1>(& source1);
        source2.OtherPart = connector->GetBridgeImpl<BridgeSource2>(& source2); 
    }
};

我认为您对CRTP有点误解。它不是虚拟调度的插件替代品。在您的情况下,IBridge没有虚拟调度,因此调用IBridge->LoadDriver将始终为您提供默认的基类实现,而不管底层派生类是什么。如果你想要一个通用接口,你需要某种虚拟调度。CRTP只是在不必要的情况下避免虚拟调度的一种方法,例如当基类成员函数调用其他虚拟函数时。

如果你想在这种情况下完全避免虚拟调度,你不能将客户端与Bridge完全解耦,你需要在Bridge上模板化client,这与在Derived上模板化相比并没有给你带来任何优势。

这是一个做你想做的事的机会。它假设您通过IBridge传递一些通用指针,Bridge使用CRTP来解释并传递给BridgeImpl。显然,在这一点上,您已经放弃了对参数类型安全性的所有希望。

class IBridge {
    virtual void LoadDriver(void *) = 0;
};
template <typename Impl>
class Bridge : public IBridge {
    void LoadDriver(void * v) override {
        static_cast<Impl*>(this)->LoadDriverImpl(*static_cast<Impl::arg_type *>(v));
    }
};
class BridgeImpl : public Bridge<BridgeImpl> {
    typedef std::string arg_type;
    void LoadDriverImpl(const std::string & s){ /*...*/ }
};

Client::Init函数中调用LoadDriver时,ValueTemplateType模板参数为std::string*,即指针。网桥实现类BridgeImplementation1BridgeImplementation2具有不采用指针的函数。因此,当编译器试图找到匹配的LoadDriver函数时,它不考虑采用非指针参数的函数,只考虑采用指针参数的功能。

您应该将BridgeImplementation1BridgeImplementation2中的LoadDriver函数更改为采用指针参数。