Question

I have been researching the Curiously Recurring Template Pattern to determine how I could use it to implement a Bridge Design Pattern.

My problem is connecting, (wiring up), the IBridgeConnector::GetBridgeImpl method to the Bridge::GetBridgeImpl method since the overriding method is actually templated.

Since virtual dispatch won't work in this case, what is the best way to point these methods to each other? Function Delegates? Is there a better pattern for this?

How should this be done?

Thanks for the help!

Made the best code simplification I can without shared_ptrs everwhere and OpenGL and DirectX calls. :) Hopefully this will be useful to someone in the future!

#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); 
    }
};
Was it helpful?

Solution

I think you have a bit of a misunderstanding of CRTP. It is not a plug in replacement for virtual dispatch. In your case, IBridge has no virtual dispatch, so calling IBridge->LoadDriver will always give you the default base class implementation, regardless of the underlying derived class. If you want a generic interface, you need some kind of virtual dispatch. CRTP is just a way of avoiding virtual dispatch in cases where it is not necessary, for example when a base class member function calls in to other virtual functions.

If you want to avoid virtual dispatch completely in you case, you can not completely decouple the client from the Bridge, you would need to template Client on Bridge which doesn't really give you any advantage over just templating on Derived.

Here's a shot at doing what you want. It assumed that you pass some generic pointer through IBridge, which Bridge uses CRTP to interpret and pass down to BridgeImpl. Obviously, you've abandoned all hope of type safety on the arguments at this point.

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

OTHER TIPS

When you call LoadDriver in the Client::Init function, the ValueTemplateType template parameter is std::string*, i.e. a pointer. The bridge implementation classes BridgeImplementation1 and BridgeImplementation2 have functions which doesn't take a pointer. So when the compiler tries to find a matching LoadDriver function, it doesn't consider the functions taking a non-pointer argument, only the one taking the pointer argument.

You should change the LoadDriver functions in BridgeImplementation1 and BridgeImplementation2 to take a pointer argument.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top