I have the following situation (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;
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);
}
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 variable 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 above minimal example. 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 a compile time expression to decide which overload to be chosen.
Update
The std::variant
(as mentioned by @lorro) would be the easiest solution here. However, require C++17 support.
However, I would like to know if we got some way around which works under the compiler flag c++11?
Note: I am working with a code-base, where external libs such as boost, can not be used, and the CRTP class structure is mostly untouchable.
std::variant<>
. Suggest using that directly. – Blenheim