Home > OS >  Is there a clean way to convert runtime values to compile time values?
Is there a clean way to convert runtime values to compile time values?

Time:01-21

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));
}

Demo

  •  Tags:  
  • Related