C++ convertible value pattern [Saturday, 2013-02-16]
This may be obvious to many C++ programmers, but I found it useful while working on gloglotto to pass angles usable in multiple conventions (degrees, radians and hours), so here it goes.
It’s basically how chrono
does in C++11.
#include <iostream>
#include <cmath>
class angle;
template <class Type>
double angle_cast (angle value);
class angle final
{
public:
class degrees final
{
public:
static angle make (double value)
{
return value;
}
};
class radians final
{
public:
static angle make (double value)
{
return value * (180 / M_PI);
}
};
class hours final
{
public:
static angle make (double value)
{
return value * (1.0 / 15.0);
}
};
private:
angle (double value)
{
_degrees = value;
}
friend class degrees;
friend class radians;
friend class hours;
template <class Type>
friend double angle_cast (angle value);
private:
double _degrees;
};
template <>
inline
double
angle_cast<angle::degrees> (angle value)
{
return value._degrees;
}
template <>
inline
double
angle_cast<angle::radians> (angle value)
{
return value._degrees * (M_PI / 180);
}
template <>
inline
double
angle_cast<angle::hours> (angle value)
{
return value._degrees * 15.0;
}
int
main (int argc, char* argv[])
{
std::cout << "10 hours in degrees: " <<
angle_cast<angle::degrees>(angle::hours::make(10)) << std::endl;
std::cout << "1 radian in degrees: " <<
angle_cast<angle::degrees>(angle::radians::make(1)) << std::endl;
return 0;
}
If we really want, we can add some C++11 goodies too, and add operators to create angles.
angle
operator "" _degrees (unsigned long long literal)
{
return angle::degrees::make(literal);
}
angle
operator "" _degrees (long double literal)
{
return angle::degrees::make(literal);
}
angle
operator "" _degree (unsigned long long literal)
{
return angle::degrees::make(literal);
}
angle
operator "" _degree (long double literal)
{
return angle::degrees::make(literal);
}
angle
operator "" _radians (unsigned long long literal)
{
return angle::radians::make(literal);
}
angle
operator "" _radians (long double literal)
{
return angle::radians::make(literal);
}
angle
operator "" _radian (unsigned long long literal)
{
return angle::radians::make(literal);
}
angle
operator "" _radian (long double literal)
{
return angle::radians::make(literal);
}
angle
operator "" _hours (unsigned long long literal)
{
return angle::hours::make(literal);
}
angle
operator "" _hours (long double literal)
{
return angle::hours::make(literal);
}
angle
operator "" _hour (unsigned long long literal)
{
return angle::hours::make(literal);
}
angle
operator "" _hour (long double literal)
{
return angle::hours::make(literal);
}
int
main (int argc, char* argv[])
{
std::cout << "10 hours in degrees: " <<
angle_cast<angle::degrees>(10_hours) << std::endl;
std::cout << "1 radian in degrees: " <<
angle_cast<angle::degrees>(1_radian) << std::endl;
return 0;
}
Quite a bit more verbose implementation, but way less verbose and clearer usage.