addressalign-toparrow-leftarrow-rightbackbellblockcalendarcameraccwcheckchevron-downchevron-leftchevron-rightchevron-small-downchevron-small-leftchevron-small-rightchevron-small-upchevron-upcircle-with-checkcircle-with-crosscircle-with-pluscrossdots-three-verticaleditemptyheartexporteye-with-lineeyefacebookfolderfullheartglobegmailgooglegroupsimageimagesinstagramlinklocation-pinm-swarmSearchmailmessagesminusmoremuplabelShape 3 + Rectangle 1outlookpersonJoin Group on CardStartprice-ribbonImported LayersImported LayersImported Layersshieldstartickettrashtriangle-downtriangle-uptwitteruseryahoo

Chicago C/C++ Users Group Message Board › A strong recommendation for numeric classes

A strong recommendation for numeric classes

Conrad W.
CWeisert
Chicago, IL
In response to a couple of strange programs that I had to review, I wrote this little article last week
http://www.idinews.co...­
As always, I welcome comments from members of our group.
Nevin ".
nliber
Libertyville, IL
Post #: 31
I would take it one step further and make Pounds and Ounces their own type (I'll leave out Grams for brevity), as in:

#ifndef WEIGHT_H_
#define WEIGHT_H_

#include <boost/operators.hpp>
#include <iosfwd>

// Forward declarations
class Pounds;

class Ounces
: boost::totally_ordered<Ounces
, boost::additive<Ounces
, boost::integer_multiplicative<Ounces,­ short
> > >
{
public:
typedef short value_type;

// Constructors
Ounces() : _oz() {}
explicit Ounces(value_type oz) : _oz(oz) {}
explicit Ounces(Pounds const& lbs);

// totally_ordered
friend bool operator==(Ounces const& lhs, Ounces const& rhs) { return lhs._oz == rhs._oz; }
friend bool operator<(Ounces const& lhs, Ounces const& rhs) { return lhs._oz < rhs._oz; }

// additive
Ounces& operator+=(Ounces const& rhs) { _oz += rhs._oz; return *this; }
Ounces& operator-=(Ounces const& rhs) { _oz -= rhs._oz; return *this; }

// multiplicative (by a scalar)
Ounces& operator*=(value_type s) { _oz *= s; return *this; }
Ounces& operator/=(value_type s) { _oz /= s; return *this; }
Ounces& operator%=(value_type s) { _oz %= s; return *this; }

// accessors
value_type get() const { return _oz; }

// streams
friend std::ostream& operator<<(std::ostream& os, Ounces const& rhs)
{ return os << rhs._oz; }

friend std::istream& operator>>(std::istream& is, Ounces& rhs)
{ return is >> rhs._oz; }

private:
friend class Pounds;

value_type _oz;
};

class Pounds
: boost::totally_ordered<Pounds
, boost::additive<Pounds
, boost::integer_multiplicative<Pounds,­ long
> > >
{
public:
typedef long value_type;

// Constructors
Pounds() : _lbs() {}
explicit Pounds(value_type lbs) : _lbs(lbs) {}
explicit Pounds(Ounces const& lbs);

// totally_ordered
friend bool operator==(Pounds const& lhs, Pounds const& rhs) { return lhs._lbs == rhs._lbs; }
friend bool operator<(Pounds const& lhs, Pounds const& rhs) { return lhs._lbs < rhs._lbs; }

// additive
Pounds& operator+=(Pounds const& rhs) { _lbs += rhs._lbs; return *this; }
Pounds& operator-=(Pounds const& rhs) { _lbs -= rhs._lbs; return *this; }

// multiplicative (by a scalar)
Pounds& operator*=(value_type l) { _lbs *= l; return *this; }
Pounds& operator/=(value_type l) { _lbs /= l; return *this; }
Pounds& operator%=(value_type l) { _lbs %= l; return *this; }

// accessors
value_type get() const { return _lbs; }

// streams
friend std::ostream& operator<<(std::ostream& os, Pounds const& rhs)
{ return os << rhs._lbs; }

friend std::istream& operator>>(std::istream& is, Pounds& rhs)
{ return is >> rhs._lbs; }

private:
friend class Ounces;

value_type _lbs;
};

inline Ounces::Ounces(Pounds const& lbs) : _oz(lbs._lbs * 16L) {}
inline Pounds::Pounds(Ounces const& oz) : _lbs(oz._oz / 16L) {}

#endif /* WEIGHT_H_ */


The goal here is to get things into types and be able to manipulate them without leaving the type system. For a weight, it makes sense to be able to add them together, or multiply by a scalar, but not to multiply two weights. It also makes sense to be able to convert between different measures of weights (Pounds and Ounces). Note: I do have a get() function to get the scalar value back out, but that is only there as an escape hatch which should be rarely (if ever) used.

What the above code doesn't allow two different measures of weights to be directly added together. I did this both because I'm not sure what the resultant type should be, as well as not wanting an implicit conversion to an intermediate type to perform the math, since that can result in an unexpected loss of precision.

If you need something richer, there are libraries which will enforce dimensional analysis.


One question about the article:

> If the program gets interrupted just after executing the first statement

Could you elaborate on what you mean by "interrupted"? For instance, if it is being interrupted by another thread, you'll need synchronization primitives (such as a mutex) to guarantee both atomicity and visibility, even if you are just accessing a single value.


I do believe that immutablility of data is a laudable goal, as it makes it much easier to reason about code, but in many ways it feels like one is fighting with C++ (over, say, a functional programming language) to achieve it.
Powered by mvnForum

Our Sponsors

People in this
Meetup are also in:

Sign up

Meetup members, Log in

By clicking "Sign up" or "Sign up using Facebook", you confirm that you accept our Terms of Service & Privacy Policy