Polymorphic 'Enums'

This is a somewhat trivial temperature scale example.

Something that I've seen used occasionally are enum classes. Something like:

enum class temperature_scale {CELSIUS, FAHRENHEIT};

However, when you'd want to use that, you'd probably want a way to convert. Say you're using this with some class, temperature, and you store the temperature internally in celsius (to easily add or compare temperatures), but you want a way to let the user pass in a temperature in a different base.

temperature::temperature(double tmp, temperature_scale scale);

And in there, you'd probably have some kind of switch statement. Or, you might realize that having switch statements everywhere is a bad idea, and simply have some kind of to_celsius() and from_celsius() function which would keep the switch statements in one place and do the conversions for you.

Something that Java enums allow for is polymorphic behavior. They can implement interfaces, so you could have to_celsius() and from_celsius() as methods on the enums themselves, and rather than some kind of switch statement, you'd just defer to the enum to do the conversion directly.

I thought about how to approximate this in C++, and I came up with
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class temperature_scale {
public:
   virtual double to_celsius(double n) const = 0;
   virtual double from_celsius(double n) const = 0;
   virtual ~temperature_scale() {}
};

namespace temperature_scales {
   const class : public temperature_scale {
   public:
      virtual double to_celsius(double n) const {
         return n - 273;
      }
      virtual double from_celsius(double n) const {
         return n + 273;
      }
   } KELVIN;
}


I only listed KELVIN, but CELSIUS and FAHRENHEIT would be provided as well. I tried this, and it seems to do what I want, and it works similarly to a Java enum:

temperature_scale& scale = temperature_scales::KELVIN;

(though obviously they are not singletons like Java enums).

I was just wondering - is there any problems with doing something like this? I haven't really seen anything like this in C++ (not that I've seen that much C++ anyway), but it seems fairly obvious to me. So, it makes me think that there's some problem with this I'm overlooking.
Last edited on
It looks a bit obtuse to me. If I was handling unit conversion routine, I'd probably just do something like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
enum class Unit{
    Foo = 0,
    //...
};

double foo_to_standard(double);
double foo_from_standard(double);
//...

typedef double (*conversion_f)(double);

static const conversion_f to_functions[] = {
    foo_to_standard,
    //..
};

static const conversion_f from_functions[] = {
    foo_from_standard,
    //..
};

double to_standard(double value, Unit unit){
    return to_functions[(int)unit](value);
}

double from_standard(double value, Unit unit){
    return from_functions[(int)unit](value);
}
Last edited on
Almost all switches can be replaced with map if you want:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace temperature
{
double to_K(double tmp, temperature_scale scale)
{
    static const std::unordered_map<temperature_scale, std::pair<double, double>> t_conv =
        {{temperature_scale::KELVIN, {0, 1}}, {temperature_scale::CELSIUS, {-273.15, 1}}, {temperature_scale::FAHRENHEIT, {-255,37, 0.556}}
        }
    const auto& constants = t_conv[scale];
    return tmp * constants.second + constants.first;
}

double from_K(double tmp, temperature_scale scale)
//Similar. Possibility to share and use same map

double convert(double temp, temperature_scale from, temperature_scale to)
{
    return from_K(to_K(temp, from), to);
}
}
Last edited on
helios is using function pointers.
Be careful so you don't forget to add the functions to the magic arrays, and don't dare to change the order of the units.
Apart for those extra measures that solution seems to be quite similar to yours

About MiiNiPaa's, it wouldn't work if you don't have a ¿linear? relationship y = ax + b
by instance, consider a logarithmic scale (like with decibels)


By the way
const temperature_scale& scale = temperature_scales::KELVIN;
your object is constant, so it cannot be referenced by a non-const reference


PS: also instead of static you may use an unnamed namespace.
Topic archived. No new replies allowed.