Reputation: 758
I have got kind of a weird use-case. Here's a very simplified version of it.
Let's say I have a class Base
and classes DerivedOne
and DerivedTwo
which are derived from the Base
class.
Then, there's a enumeration:
enum DerClasses {Derived1, Derived2};
and a function which would take a enum and return the instance of derived class, depending on the value.
Something like:
inline Base* create_instance(DerClasses enum_class){
switch(enum_class) {
case Derived1:
return new Derived1();
case Derived2:
return new Derived2();
}
}
Obviously this works, but only with the cast to the derived class afterwards.
Derived1 *derived_pointer = dynamic_cast<Derived1*>(pointer);
And I don't want users to make these dynamic casts themselves or even know anything about the classes.
Is that possible to somehow hide these casts and make an API with automatic type deduction, like
auto ptr = create_instance(DerClasses::Derived1);
ptr->derived1_class_only_variable = 123;
Upvotes: 0
Views: 495
Reputation: 4050
You can do it by using some meta-programming. If you are using C++ 11/14 you can do something like this:
enum class ClassType {
FirstType,
SecondType,
ThirdType
};
namespace internal {
template <ClassType Type>
struct _build_type {};
template <>
struct _build_type<ClassType::FirstType> {
constexpr auto operator()() {
return std::make_unique<MyFirstType>();
}
};
template <>
struct _build_type<ClassType::SecondType> {
constexpr auto operator()() {
return std::make_unique<MySecondType>();
}
};
template <>
struct _build_type<ClassType::ThirdType> {
constexpr auto operator()() {
return std::make_unique<MyThirdType>();
}
};
}
template <WindowType Type>
constexpr auto create_instance() {
return internal::_build_type<Type>{}();
}
And then your code becomes exactly, what you are saying:
auto ptr = create_instance<ClassType::FirstType>();
ptr->derived1_class_only_variable = 123;
In modern C++17, you can simplify the code with a simple constexpr if
condition.
Upvotes: 2
Reputation: 275976
template<DerClasses enum_class>
auto create_instance(){
if constexpr (enum_class == DerClasses::Derived1) {
return std::make_unique<Derived1>();
else if constexpr (enum_class == DerClasses::Derived2) {
return std::make_unique<Derived2>();
}
this is like c++17. Doing this in c++11 is very annoying.
If you don't know the DerClasses
value at compile time, this does not work, and cannot work. The closest you can get is continuation passing style:
template<class F>
decltype(auto) create_instance(DerClasses enum_class, F&& f){
switch(enum_class) {
case DerClasses::Derived1:
return f(std::make_unique<Derived1>());
case DerClasses::Derived2:
return f(std::make_unique<Derived2>());
}
}
which is used like:
create_instance(DerClasses::Derived1, [&](auto&& ptr) {
if constexpr( std::is_same< std::decay_t<decltype(*ptr)>, Derived1 >{} )
ptr->derived1_class_only_variable = 123;
});
but that sucks too, because the lambda is invoked with both derived types; only one is run.
A use of override could work, but again you are getting crazy.
Upvotes: 3