Я работаю над созданием оболочки, чтобы можно было легко переносить будущий код на разные движки рендеринга.В настоящее время мы работаем в 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?