Я работаю над созданием оболочки, чтобы иметь возможность легко переносить будущий код на разные серверные механизмы рендеринга. В настоящее время мы работаем в GDI. В настоящее время я реализую виртуальные функции на абстрактном бэкэнде, но я хотел бы изменить это на CRTP, поскольку бэкэнд должен быть известен во время компиляции.
К сожалению, одна проблема, с которой я столкнулся при работе с CRTP (в первый раз), заключается в том, что я должен реализовать все детали производных функций. Напротив, абстрактная реализация не требует полностью реализованных производных дочерних элементов. Для демонстрации рассмотрим следующее:
#include <Windows.h>
#include <iostream>
struct AbstractBackend
{
virtual ~AbstractBackend() = 0;
virtual void foo()
{
throw "implementation missing: failed to override in derived class";
}
virtual void bar()
{
throw "implementation missing: failed to override in derived class";
}
};
AbstractBackend::~AbstractBackend() {}
struct ConcreteBackendA : AbstractBackend
{
int backendResource;
ConcreteBackendA(int rsc) :
backendResource(rsc)
{}
virtual void foo()
{
printf("executing ConcreteBackendA::foo!\n");
}
// ConcreteBackendA does not support "bar" feature
};
struct ConcreteBackendB : AbstractBackend
{
HDC backendResource;
ConcreteBackendB(HDC hdc) :
backendResource(hdc)
{}
virtual void foo()
{
printf("executing ConcreteBackendB::foo!\n");
}
virtual void bar()
{
printf("executing ConcreteBackendB::bar!\n");
}
};
struct FrontEnd
{
AbstractBackend *backend;
FrontEnd(int rsc) :
backend(new ConcreteBackendA(rsc))
{}
FrontEnd(HDC hdc) :
backend(new ConcreteBackendB(hdc))
{}
~FrontEnd()
{
delete backend;
}
void foo()
{
backend->foo();
}
void bar()
{
backend->bar();
}
};
int main()
{
int rsc = 0;
HDC hdc = 0;
FrontEnd A(rsc);
FrontEnd B(hdc);
A.foo();
A.bar(); // throws an error, A::bar is not a feature of this engine
B.foo();
B.bar();
std::cin.get();
}
В этом примере AbstractBackend поддерживает две функции: foo и bar. ConcreteBackendA поддерживает только foo, bar — это функция, которую он не поддерживает (может быть, что-то вроде Draw3dText), но это нормально. Пользователь может поймать исключения и двигаться дальше. Одним небольшим недостатком является использование виртуальных функций. Я хотел бы развлечь мысль об использовании CRTP следующим образом:
#include <Windows.h>
#include <iostream>
template <class Derived>
struct AbstractBackend
{
virtual ~AbstractBackend() = 0;
void foo()
{
static_cast<Derived*>(this)->foo();
}
void bar()
{
static_cast<Derived*>(this)->bar();
}
};
template <class Derived>
AbstractBackend<Derived>::~AbstractBackend() {}
struct ConcreteBackendA : AbstractBackend<ConcreteBackendA>
{
int backendResource;
ConcreteBackendA(int rsc) :
backendResource(rsc)
{}
void foo()
{
printf("executing ConcreteBackendA::foo!\n");
}
// ConcreteBackendA does not support "bar" feature
};
struct ConcreteBackendB : AbstractBackend<ConcreteBackendB>
{
HDC backendResource;
ConcreteBackendB(HDC hdc) :
backendResource(hdc)
{}
void foo()
{
printf("executing ConcreteBackendB::foo!\n");
}
void bar()
{
printf("executing ConcreteBackendB::bar!\n");
}
};
template <class ConcreteBackend>
struct FrontEnd
{
AbstractBackend<ConcreteBackend> *backend;
FrontEnd(int rsc) :
backend(new ConcreteBackendA(rsc))
{}
FrontEnd(HDC hdc) :
backend(new ConcreteBackendB(hdc))
{}
~FrontEnd()
{
delete backend;
}
void foo()
{
backend->foo();
}
void bar()
{
backend->bar();
}
};
int main()
{
int rsc = 0;
HDC hdc = 0;
FrontEnd<ConcreteBackendA> A(rsc);
FrontEnd<ConcreteBackendB> B(hdc);
A.foo();
A.bar(); // no implementation: stack overflow
B.foo();
B.bar();
std::cin.get();
}
Проблема в том, что если производный класс не может реализовать функцию из AbstractBackend, тогда AbstractBackend вызовет сам себя, вызывая переполнение стека.
Как я могу воспроизвести поведение виртуальной абстрактной реализации с помощью CRTP?
if(frontEnd.CanDo3DText()) frontEnd.Draw3dText(params)
. Я не знаю, почему вы настаиваете на объединении попытки сделать что-то с выполнением самого дела. - person Nicol Bolas   schedule 01.02.2019