'C++: Is there a less verbose way represent custom numeric types?
I need to represent Volts, Ampers, And Watts and their relations (e.g. W = V * I)..
This is what I came with, but it seems really verbose. Any idea on how to make it more concise ?
#include <stdio.h>
#include <iostream>
template<class T>
class double_val {
public:
explicit double_val(double val):_val(val) { };
double_val(const T& other) {
_val = other._val;
}
T& operator=(const T &other) {
_val = other._val;
return *this;
}
T operator+ (const T& other) const {
return T(this->_val + other._val);
}
T operator+ (double val) const {
return T(this->_val + val);
}
T operator- (const T& other) const {
return T(this->_val - other._val);
}
T operator- (double val) const {
return T(this->_val - val);
}
T operator* (const T& other) const {
return T(this->_val * other._val);
}
T operator* (double val) const {
return T(this->_val * val);
}
T operator/ (const T& other) const {
return T(this->_val / other._val);
}
T operator/ (double val) const {
return T(this->_val / val);
}
bool operator== (const T& other) const{
return this->_val == other._val;
}
bool operator!= (const T& other) const{
return this->_val != other._val;
}
bool operator > (const T& other) const{
return this->_val > other._val;
}
bool operator >= (const T& other) const{
return this->_val >= other._val;
}
bool operator < (const T& other) const{
return this->_val < other._val;
}
bool operator <= (const T& other) const{
return this->_val <= other._val;
}
void val(double val){
_val = val;
}
double val() const {
return _val ;
}
virtual const char* name() const = 0;
private:
double _val;
};
template<class T>
std::ostream& operator<<(std::ostream &os, const double_val<T> &t) {
return os << t.val() << " " << t.name();
}
class Amper:public double_val<Amper> {
public:
Amper(double val):double_val<Amper>(val) {}
const char* name() const {
return "Amper";
}
};
class Volt:public double_val<Volt> {
public:
Volt(double val):double_val<Volt>(val) {}
const char* name() const {
return "Volt";
}
};
class Watt:public double_val<Watt> {
public:
Watt(double val):double_val<Watt>(val) {}
const char* name() const {
return "Watt";
}
};
// Watt = I * V
Watt operator* (const Volt &v, const Amper& a) {
return Watt(a.val() * v.val());
}
Watt operator* (const Amper &a, const Volt& v) {
return Watt(a.val() * v.val());
}
// Volts = Watts/Ampers
Volt operator / (const Watt &w, const Amper& a) {
return Volt(w.val() / a.val());
}
// Ampers = Watts/Volts
Amper operator / (const Watt &w, const Volt& v) {
return Amper(w.val() / v.val());
}
int main(int argc, char **argv) {
using namespace std;
Watt w = Volt(66) * Amper(7);
Amper a = w / (Volt(646) * Volt(444));
cout << a << endl;
return 0;
}
(note: I intentionaly omitted operator double(), since I wanted to avoid forbidden ops like Volt(4) + Watt(6))
Solution 1:[1]
Boost.Units is a good solution for your needs, as it provides a respresentation of volts, amps, watts and appropriate operators out of the box.
Boost.Operators solves this problem for the general case of custom numeric types that are not SI units.
Solution 2:[2]
If client code cooperates, you can remove logical operations using std::rel_ops. rel_ops defines a bunch of logical operators in terms of your == and < operators.
However this is problematic -- it forces the client to use rel_ops, and rel_ops suddenly lets any class with an == and < operator to be compared with all the other operators. This is probably not what you want.
IMO the better option is to use the boost operators library, which seems ideal for numerical types as it provides all the various operators you could want for a numerical type. You simply define a few basic operators, and it fills in the rest based on your specification. To steal from the boost documentation's example:
template <class T>
class point // note: private inheritance is OK here!
: boost::addable< point<T> // point + point
, boost::subtractable< point<T> // point - point
, boost::dividable2< point<T>, T // point / T
, boost::multipliable2< point<T>, T // point * T, T * point
> > > >
{
public:
point(T, T);
T x() const;
T y() const;
point operator+=(const point&);
// point operator+(point, const point&) automatically
// generated by addable.
point operator-=(const point&);
// point operator-(point, const point&) automatically
// generated by subtractable.
point operator*=(T);
// point operator*(point, const T&) and
// point operator*(const T&, point) auto-generated
// by multipliable.
point operator/=(T);
// point operator/(point, const T&) auto-generated
// by dividable.
private:
T x_;
T y_;
};
Solution 3:[3]
Also, Boost Utility contains facilities that greatly simplify writing all the operator plumbing in such cases.
I have recently done a sample here: Can you set a maximum limit to an integer (C++)?
Solution 4:[4]
If you want something more lightweight than boost you can use TU (Typesafe Units). This is a header-only library based on c++20 that supports manipulation (*, /, +, -, sqrt, pow to arbitrary floating point numbers, unary operations on scalar units) for all SI units. It supports units with floating point dimensions like length^(d) where d is a decimal number. Moreover it is simple to define your own units. It also comes with a test suite...
... note that I'm the author of this library.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | JoeG |
| Solution 2 | Doug T. |
| Solution 3 | Community |
| Solution 4 | marcks |
