Reputation: 33
I am trying to learn how to use c++11 user defined literals for units of physical properties. The question is, how do I avoid a mixing of these units. So that (8.0_kg + 8.0_km)--> gives error. any ideas guys? i am new to c++, be kind.
class Mass{
public:
//Mass(){
// cout << "only Mass units allowed in here" << endl;
//}
//~Mass();
long double getWeight(long double a);
double car, house, cat;
private:
long double a;
};
long double Mass::getWeight(long double w) {
cout << "returning argument: " << w << '\n'<< endl;
return 0;
}
long double operator"" _km(long double d) { return d * 1000.0; }
long double operator"" _m (long double d) {return d;}
long double operator"" _cm(long double d) { return d / 100.0; }
long double operator"" _tonne(long double m) { return m * 1000.0 ; }
long double operator"" _kg(long double m) { return m ; }
long double operator"" _lb(long double m) { return m * 0.453592; }
long double getDistance(long double d){
long double starting_d = 61.0_kg;
long double total_d = d + starting_d;
cout << "the distance I have run is: " << total_d << endl;
return 0;
}
int main() {
cout << 6.0_km << endl;
cout << 6.0_km + 3.0_m << endl;
cout << 6.0_km + 3.0_m + 15.0_cm << '\n' << endl;
cout << 8.0_tonne << endl;
cout << 8.0_km + 4.0_kg << endl;
cout << 8.0_km + 4.0_kg + 21.0_lb << '\n' << endl;
long double distance = 5.45_km;
getDistance(distance);
Mass obj1;
obj1.getWeight(13.96_lb);
cout << "This is clearly wrong: "<< 8.0_km + 4.0_kg << endl;
obj1.getWeight(10.96_km); // so is this
}
Upvotes: 2
Views: 620
Reputation: 1681
Create classes representing numeric values of the different units. That's how it's been done since long before C++ 11.
Custom literals can make instantiation more readable, though, because it helps preserve the usual order of number and unit :)
See http://en.cppreference.com/w/cpp/language/user_literal
class MassKg
{
double value;
// public c'tor, numeric operators, &c.
};
// ...
MassKg mass(5.0);
DistanceM distance(3.0);
auto c = mass * distance; // may yield an instance of TorqueKgM, or MomentumKgM, therefore
// explicit functions / methods are preferrable for mixed
// multiplication or division
auto mass2 = mass + MassKg(2.0); // yiels an instance of MassKg
auto invalid = mass + distance; // compile time error
Upvotes: 2
Reputation: 66371
You need to define your own types, since you can't restrict what a primitive represents.
You can use a "tagged template"1 to avoid repetition of operators and such and keep it type safe.
This can be extended so you get for instance distance * distance = area
or speed * time = distance
checked by the compiler.
Here's a short example:
template<typename Kind>
struct Value
{
long double value;
Value& operator+= (Value v) { value += v.value; return *this; }
};
template <typename Kind>
Value<Kind> operator+ (Value<Kind> lhs, Value<Kind> rhs) { return lhs += rhs; }
// These types don't need definitions; we only need some unique type names.
struct M;
struct D;
using Mass = Value<M>;
using Distance = Value<D>;
Mass operator"" _kg(long double d) { return { d };}
Mass operator"" _lb(long double d) { return { d * 0.453592 };}
Distance operator"" _km(long double d) { return { d * 1000 };}
Distance operator"" _mile(long double d) { return { d * 1609 };}
int main()
{
// OK
Distance d = 1.2_km + 0.2_mile;
// OK
Mass m = 2.3_kg + 1.4_lb;
// invalid operands to binary expression ('Distance' (aka 'Value<D>')
// and 'Mass' (aka 'Value<M>'))
Distance d2 = 2.4_km + 1.2_kg; // Nope
}
1) I don't think there's an established term in C++, but it's very similar to what Haskell refers to as phantom types.
Upvotes: 9