Как использовать любопытно повторяющийся шаблон для Bridge Pattern?

Я исследовал шаблон Curly Recurring Template Pattern, чтобы определить, как его использовать для реализации шаблона проектирования моста.

Моя проблема заключается в подключении (связывание) метода IBridgeConnector :: GetBridgeImpl к методу Bridge :: GetBridgeImpl, поскольку метод переопределения фактически является шаблонным.

Поскольку виртуальная диспетчеризация в этом случае не будет работать, каков наилучший способ указать эти методы друг другу? Функция делегатов? Есть ли лучший шаблон для этого?

Как это должно быть сделано?

Спасибо за помощь!

Сделать лучшее упрощение кода, которое я могу, без shared_ptrs где-либо и 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);
}
};

1

Решение

Я думаю, что у вас есть некоторое недопонимание CRTP. Это не замена плагина для виртуальной отправки. В вашем случае IBridge не имеет виртуальной диспетчеризации, поэтому вызов IBridge-> LoadDriver всегда даст вам реализацию базового класса по умолчанию, независимо от базового производного класса. Если вам нужен универсальный интерфейс, вам нужна виртуальная диспетчеризация. CRTP — это просто способ избежать виртуальной диспетчеризации в тех случаях, когда в этом нет необходимости, например, когда функция-член базового класса вызывает другие виртуальные функции.

Если вы хотите полностью избежать виртуальной диспетчеризации в вашем случае, вы не можете полностью отделить клиент от Bridge, вам нужно будет создать шаблон Client on Bridge, который на самом деле не дает никаких преимуществ по сравнению с простыми шаблонами в 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){ /*...*/ }
};
3

Другие решения

Когда вы звоните LoadDriver в Client::Init функция, ValueTemplateType параметр шаблона std::string*то есть указатель. Классы реализации моста BridgeImplementation1 а также BridgeImplementation2 есть функции, которые не взять указатель Поэтому, когда компилятор пытается найти соответствующий LoadDriver функция, она не учитывает функции, принимающие аргумент без указателя, а только функцию, принимающую аргумент указателя.

Вы должны изменить LoadDriver функции в BridgeImplementation1 а также BridgeImplementation2 принять аргумент указателя.

3