I'm working on making a wrapper to be able to port future code easily to different backend rendering engines. We are currently working in GDI. Currently I am implementing virtual functions on an abstract backend, but I'd like to change that to CRTP since the backend should be known at compile time.
Unfortunately one hiccup I've experienced with CRTP (first time using) is that I must implement all details of derived functions. In contrast, the abstract implementation does not require fully implemented derived children. To demonstrate consider this:
#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();
}
In this example, the AbstractBackend supports two features, foo & bar. The ConcreteBackendA only supports foo, bar is a function that it cannot support (maybe something like Draw3dText), but that's ok. The user can catch the exceptions and move on. One small drawback is the usage of virtual functions. I'd like to entertain the thought of using CRTP like this:
#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();
}
The problem is that if a derived class fails to implemented a function from the AbstractBackend, then the AbstractBackend will call itself causing a stack overflow.
How can I replicated the behavior of the virtual abstract implementation with CRTP?
Aucun commentaire:
Enregistrer un commentaire