I'd like to create a type with non-type parameters based on run-time values, the make_fruit function:
struct IFruit {
// interface
};
enum Species {
Apple,
Orange,
Peach,
};
enum Color {
Red,
Green,
Blue,
Yellow,
};
template <Species S, Color C> struct Fruit : public IFruit {
// impl
};
IFruit* make_fruit(Species s, Color c) {
// return new Fruit<s, c>
}
Is there a clean way to do it? As in, not writing 12 switch cases.
Edit: note: the classes IFruit, Fruit and enums Color, Species are given (by some library) and cannot be modified.
CodePudding user response:
Short answer: No!
Long answer: Still no, but you can make you life easier by creating intermediate nested functions. Each function is templated for the enums you already treated and have a switch for a single enum. So instead of writing 12 condition, you would write only 7 in your case, and the more values exists, the more you'll save with this approach.
template <Species s>
IFruit* make_fruit_of_species(Color c) {
//your switch for color here
}
IFruit* make_fruit(Species s, Color c) {
// your switch for species here, returning a value with make_fruit_of_species
}
But I wonder if you really need a template for such a simple case. Maybe you just need a member of type species and one of type color.
CodePudding user response:
You can avoid to write the combinatory yourself thanks to std::visit of std::variant:
using SpeciesVariant = std::variant<
std::integral_constant<Species, Species::Apple>,
std::integral_constant<Species, Species::Orange>,
std::integral_constant<Species, Species::Peach>
>;
using ColorVariant = std::variant<
std::integral_constant<Color, Color::Red>,
std::integral_constant<Color, Color::Green>,
std::integral_constant<Color, Color::Blue>,
std::integral_constant<Color, Color::Yellow>
>;
SpeciesVariant to_variant(Species s)
{
switch (s) {
case Species::Apple: return std::integral_constant<Species, Species::Apple>{};
case Species::Orange: return std::integral_constant<Species, Species::Orange>{};
case Species::Peach: return std::integral_constant<Species, Species::Peach>{};
}
throw std::runtime_error("Bad argument");
}
ColorVariant to_variant(Color c)
{
switch (c) {
case Color::Red: return std::integral_constant<Color, Color::Red>{};
case Color::Green: return std::integral_constant<Color, Color::Green>{};
case Color::Blue: return std::integral_constant<Color, Color::Blue>{};
case Color::Yellow: return std::integral_constant<Color, Color::Yellow>{};
}
throw std::runtime_error("Bad argument");
}
and finally:
std::unique_ptr<IFruit> make_fruit(Species s, Color c)
{
return std::visit([](auto s, auto c) -> std::unique_ptr<IFruit> {
return std::make_unique<Fruit<s, c>>();
}, to_variant(s), to_variant(c));
}
