I have a CRTP implementation for calling the corresponding implementations via base interface. I use the child classes instances as member of the SomeLogicClass
class, so that I can call corresponding doSomething_Impl
via interface call from the Base
class.
The problem is that the child class selection (to which the doSomething_Impl()
must be called), should be taken place by deciding upon a run time value mClassId
of the SomeLogicClass
.
The only two possible solutions I can think of are a normal switch case and SFINAE the member functions, as described in the following minimal example. (live code : https://gcc.godbolt.org/z/d8jG9bs9a)
#include <iostream>
#include <type_traits>
#define ENBALE true // to enable disable test solutions
enum struct Type : unsigned { base = 0, child1, child2, child3 /* so on*/ };
// CRTP Base
template<typename Child> struct Base {
void doSomething() { static_cast<Child*>(this)->doSomething_Impl(); }
private:
Base() = default;
friend Child;
};
struct Child1 : public Base<Child1> {
void doSomething_Impl() { std::cout << "Child1 implementation\n"; }
};
struct Child2 : public Base<Child2> {
void doSomething_Impl() { std::cout << "Child2 implementation\n"; }
};
struct Child3 : public Base<Child3> {
void doSomething_Impl() { std::cout << "Child3 implementation\n"; }
};
// ... so on
class SomeLogicClass
{
Type mClassId{ Type::base };
Child1 mChild1;
Child2 mChild2;
Child3 mChild3;
// ... child3 so on!
public:
Type getId() const { return mClassId; }
void setId(Type id) { mClassId = id; } // run time depended!
#if ENBALE // Solution 1 : simple case
/*what in C++11?*/ getInstance()
{
switch (mClassId)
{
case Type::child1: return mChild1;
case Type::child2: return mChild2;
case Type::child3: return mChild3;
default: // error case!
break;
}
}
#elif !ENBALE // Solution 2 : SFINAE
template<Type ID>
auto getInstance() -> typename std::enable_if<ID == Type::child1, Child1&>::type { return mChild1; }
template<Type ID>
auto getInstance() -> typename std::enable_if<ID == Type::child2, Child2&>::type { return mChild2; }
template<Type ID>
auto getInstance() -> typename std::enable_if<ID == Type::child3, Child3&>::type { return mChild3; }
#endif
};
void test(SomeLogicClass& ob, Type id)
{
ob.setId(id);
#if ENBALE // Solution 1
auto& childInstance = ob.getInstance();
#elif !ENBALE // Solution 2
auto& childInstance = ob.getInstance<ob.getId()>();
#endif
childInstance.doSomething(); // calls the corresponding implementations!
}
int main()
{
SomeLogicClass ob;
test(ob, Type::child1);
test(ob, Type::child2);
test(ob, Type::child3);
}
As noted in the comments in the above code, both can't get work, for the reasons
- Solution 1: the member function must have a unique return type
- Solution 2: SFINAE required compile time expression to decide which overload to be chosen.
Dynamic polymorphism would be the easiest solution here (Solution 1 works with Base&
). However, I would like to know if we got some way around still keeping the static polymorphism under the compiler flag c++11?
I checked this already given answer: Optimize template replacement of a switch however, I could not figure the actual application in my case.
Even though I am looking for something compilable under C++11, it might be interesting for future readers to know something working in the latest standards as well.
Aucun commentaire:
Enregistrer un commentaire