/*
===========================
Boring Introductory Stuff
===========================
*/
#define _DIMENSIONS_H_
#include <vector>
#include <string>
#include <sstream>
#include <cmath>
#include <algorithm>
#ifndef tstring
#ifdef _UNICODE
typedef std::wstring tstring;
typedef std::wistringstream tistringstream;
typedef std::wostringstream tostringstream;
typedef std::wostream tostream;
typedef std::wistream tistream;
#else
typedef std::string tstring;
typedef std::istringstream tistringstream;
typedef std::ostringstream tostringstream;
typedef std::ostream tostream;
typedef std::istream tistream;
#endif // _UNICODE
#endif // tstring
//Some useful typedefs from the Windows SDK.
//Since these are defined in WinNT.h, I'll take the chance of using this condition:
#if !(defined(_WINNT_) || defined(_TSTR_DEFINED_))
#define _TSTR_DEFINED_
#ifdef _UNICODE
typedef wchar_t TCHAR;
#else
typedef char THCAR;
#endif // _UNICODE
typedef TCHAR *LPTSTR;
typedef TCHAR const *LPCTSTR;
#endif // !(defined(_WINNT_) || defined(_TSTR_DEFINED_))
//If tchar.h has been included, the following is defined already.
#ifndef _T
#ifdef _UNICODE
#define __T(x) L##x
#else
#define __T(x) x
#endif // _UNICODE
#define _T(x) __T(x)
#endif // _T
#ifndef NULL
#define NULL 0
#endif // NULL
/*
===============================
END Boring Introductory Stuff
===============================
*/
#define LIBNS Units
namespace LIBNS
{
/*
===============
Basic Classes
===============
*/
class FactorBase
{
//Constructors, Destructor
protected:
FactorBase()
{ }
virtual ~FactorBase()
{ }
//Public Interface
public:
virtual double GetFactor() const { return 1; }
virtual LPCTSTR GetName() const { return NULL; }
virtual LPCTSTR GetSymbol() const { return NULL; }
};
class Prefix : public FactorBase
{
//Member Data
private:
double m_convFactor;
tstring m_name;
tstring m_symbol;
//Constructors
private:
Prefix(Prefix const&); //No copying.
Prefix(double convFactor = 1.0, LPCTSTR name = _T(""), LPCTSTR symbol = _T(""))
: m_convFactor(convFactor), m_name(name), m_symbol(symbol), FactorBase()
{ }
//Public Interface
public:
virtual double GetFactor() const { return m_convFactor; }
virtual LPCTSTR GetName() const { return m_name.c_str(); }
virtual LPCTSTR GetSymbol() const { return m_symbol.c_str(); }
//Operators
private:
const Prefix& operator=(Prefix const&); //No assignment.
//Static Members
public:
static Prefix const NoPrefix;
static Prefix const Deca;
static Prefix const Hecto;
static Prefix const Kilo;
static Prefix const Mega;
static Prefix const Giga;
static Prefix const deci;
static Prefix const centi;
static Prefix const milli;
static Prefix const micro;
static Prefix const nano;
};
Prefix const Prefix::NoPrefix = Prefix();
Prefix const Prefix::Deca(10, _T("Deca"), _T("D"));
Prefix const Prefix::Hecto(100, _T("Hecto"), _T("H"));
Prefix const Prefix::Kilo(1000, _T("Kilo"), _T("k"));
Prefix const Prefix::Mega(1000000, _T("Mega"), _T("M"));
Prefix const Prefix::Giga(1e9, _T("Giga"), _T("G"));
Prefix const Prefix::deci(0.1, _T("deci"), _T("d"));
Prefix const Prefix::centi(0.01, _T("centi"), _T("c"));
Prefix const Prefix::milli(0.001, _T("milli"), _T("m"));
Prefix const Prefix::micro(0.000001, _T("micro"), _T("u"));
Prefix const Prefix::nano(1e-9, _T("nano"), _T("n"));
class Unit : public FactorBase
{
protected:
Prefix const *m_prefix;
//Buffer to hold the symbol so the return value of GetSymbol() can be LPCTSTR.
mutable tstring _symbol;
//Constructors
protected:
Unit(Prefix const &prefix = Prefix::NoPrefix) : m_prefix(&prefix)
{ }
Unit(Unit const &src) : m_prefix(src.m_prefix)
{ }
//Helpers
protected:
virtual LPCTSTR GetSymbolNoPrefix() const { return _T(""); }
//Public Interface
public:
virtual Unit* Clone() const { return NULL; }
virtual double GetFactor() const { return m_prefix->GetFactor(); }
virtual bool IsStandardUnit() const { return false; }
virtual bool HasPrefixAffinity() const { return true; }
virtual double GetFactorNoPrefix() const { return GetFactor() / m_prefix->GetFactor(); }
virtual LPCTSTR GetSymbol() const
{
if (!_symbol.size())
{
_symbol = tstring(m_prefix->GetSymbol()) + GetSymbolNoPrefix();
}
return _symbol.c_str();
}
//Operators
public:
Unit& operator=(Unit const &op2)
{
m_prefix = op2.m_prefix;
_symbol.clear();
return *this;
}
//Dimensionless singleton
public:
static const Unit Dimensionless;
};
const Unit Unit::Dimensionless = Unit();
class UnitContext
{
//Member data.
private:
double m_exponent;
Unit *m_unit;
//Constructors
public:
UnitContext(UnitContext const &src) : m_exponent(src.m_exponent), m_unit(src.m_unit)
{ }
UnitContext(Unit const &unit, double exponent) : m_exponent(exponent), m_unit(const_cast<Unit*>(&unit))
{ }
//Public Interface
public:
double GetExponent() const { return m_exponent; }
void SetExponent(double newExponent) { m_exponent = newExponent; }
Unit const& GetUnit() const { return *m_unit; }
UnitContext& operator=(UnitContext const &op2)
{
m_exponent = op2.m_exponent;
m_unit = op2.m_unit;
return *this;
}
};
class CompoundUnit : public Unit
{
//Internal data.
protected:
std::vector<UnitContext> _units;
//Constructors
public:
CompoundUnit() : Unit()
{ }
//Private functor to calculate the accumulated conversion factor.
private:
class _ConvFactorF
{
//Internal Data
private:
double _accumFactor;
//Constructor
public:
_ConvFactorF(double initialValue = 1.0) : _accumFactor(initialValue)
{ }
//Public Interface
public:
double GetAccumulatedConversionFactor() { return _accumFactor; }
//Operator overloads
void operator()(UnitContext const &uc)
{
_accumFactor *= pow(uc.GetUnit().GetFactor(), uc.GetExponent());
}
};
//Private functor to derivate the symbol of the compound unit.
private:
class _SymbolAssemblerF
{
//Internal Data
private:
tostringstream _num_os;
tostringstream _den_os;
bool _useExponentChar;
//Constructors
public:
_SymbolAssemblerF(_SymbolAssemblerF const &src) //Copy constructor needed because the stupid for_each() will clone this.
: _num_os(src._num_os.str())
, _den_os(src._den_os.str())
, _useExponentChar(src._useExponentChar)
{ }
_SymbolAssemblerF(bool useExponentChar = false) : _useExponentChar(useExponentChar)
{ }
//Public Interface
public:
tstring GetAccummulatedSymbol()
{
tstring symbol = _num_os.str();
tstring den = _den_os.str();
if (den.size())
{
symbol += _T('/');
symbol += den;
}
return symbol;
//return tstring(_T("Nothing"));
}
//Operator Overloads
void operator()(UnitContext const &uc)
{
tostringstream &s = uc.GetExponent() < 0 ? _den_os : _num_os;
s << uc.GetUnit().GetSymbol();
if (abs(uc.GetExponent()) != 1)
{
if (_useExponentChar) s << _T('^');
s << abs(uc.GetExponent());
}
}
};
//Helpers
protected:
virtual Unit* Clone() const { return new CompoundUnit(); }
//Public Interface
public:
double GetFactor() const
{
_ConvFactorF f = std::for_each(_units.begin(), _units.end(), _ConvFactorF());
return f.GetAccumulatedConversionFactor();
}
LPCTSTR GetSymbol() const
{
_SymbolAssemblerF f = std::for_each(_units.begin(), _units.end(), _SymbolAssemblerF());
return f.GetAccummulatedSymbol().c_str();
}
};
/*
===================
END Basic Classes
===================
*/
/*
==============
Length Units
==============
*/
//Forward declaration of Length for friend purposes.
class Length;
class LengthUnit : public Unit
{
friend class Length;
//Constructors
protected:
LengthUnit() : Unit()
{ }
//Static Members
public:
static const LengthUnit &StandardUnit;
};
class MeterUnit : public LengthUnit
{
//Constructors
public:
MeterUnit() : LengthUnit()
{ }
//Helpers
protected:
virtual LPCTSTR GetSymbolNoPrefix() const { return _T("m"); }
virtual Unit* Clone() const { return new MeterUnit(); }
//Public Interface.
public:
virtual bool IsStandardUnit() const { return true; }
virtual LPCTSTR GetName() const { return _T("meter"); }
static const MeterUnit Meter;
};
const MeterUnit MeterUnit::Meter = MeterUnit();
const LengthUnit &LengthUnit::StandardUnit = MeterUnit::Meter;
class FootUnit : public LengthUnit
{
//Constructors
public:
FootUnit() : LengthUnit()
{ }
//Helpers
protected:
virtual LPCTSTR GetSymbolNoPrefix() const { return _T("ft"); }
virtual Unit* Clone() const { return new FootUnit(); }
//Public Interface
public:
virtual double GetFactor() const { return 0.3048; }
virtual LPCTSTR GetName() const { return _T("foot"); }
//Singleton
public:
static const FootUnit Foot;
};
const FootUnit FootUnit::Foot = FootUnit();
/*
====================
END Length Units
====================
*/
/*
============
Time Units
============
*/
//Forward declaration of Time for friends purposes.
class Time;
class TimeUnit : public Unit
{
friend class Time;
//Constructors
protected:
TimeUnit() : Unit()
{ }
//Static Members
public:
static TimeUnit const &StandardUnit;
};
class SecondUnit : public TimeUnit
{
//Constructors
private:
SecondUnit() : TimeUnit()
{ }
//Helpers
protected:
virtual LPCTSTR GetSymbolNoPrefix() const { return _T("s"); }
virtual Unit* Clone() const { return new SecondUnit(); }
//Public Interface
public:
virtual bool IsStandardUnit() const { return true; }
virtual LPCTSTR GetName() const { return _T("second"); }
static const SecondUnit Second;
};
const SecondUnit SecondUnit::Second = SecondUnit();
const TimeUnit &TimeUnit::StandardUnit = SecondUnit::Second;
class HourUnit : public TimeUnit
{
//Constructors
private:
HourUnit() : TimeUnit()
{ }
//Helpers
virtual LPCTSTR GetSymbolNoPrefix() const { return _T("h"); }
virtual Unit* Clone() const { return new HourUnit(); }
//Public Interface
public:
virtual double GetFactor() const { return 3600; }
virtual LPCTSTR GetName() const { return _T("hour"); }
//Singleton
public:
static HourUnit const Hour;
};
const HourUnit HourUnit::Hour = HourUnit();
/*
================
END Time Units
================
*/
/*
=============
Speed Units
=============
*/
class SpeedUnit : public CompoundUnit
{
//Constructors
public:
SpeedUnit() : CompoundUnit()
{ }
public:
static SpeedUnit const &StandardUnit;
};
class MetersPerSecondUnit : public SpeedUnit
{
//Constructors
private:
MetersPerSecondUnit() : SpeedUnit()
{
_units.push_back(UnitContext(MeterUnit::Meter, 1));
_units.push_back(UnitContext(SecondUnit::Second, -1));
}
//Singleton
public:
static MetersPerSecondUnit const MetersPerSecond;
};
const MetersPerSecondUnit MetersPerSecondUnit::MetersPerSecond = MetersPerSecondUnit();
const SpeedUnit &SpeedUnit::StandardUnit = MetersPerSecondUnit::MetersPerSecond;
class FeetPerSecondUnit : public SpeedUnit
{
//Constructors
public:
FeetPerSecondUnit() : SpeedUnit()
{
_units.push_back(UnitContext(FootUnit::Foot, 1));
_units.push_back(UnitContext(SecondUnit::Second, -1));
}
//Singleton
public:
static FeetPerSecondUnit const FeetPerSecond;
};
const FeetPerSecondUnit FeetPerSecondUnit::FeetPerSecond = FeetPerSecondUnit();
/*
=================
END Speed Units
=================
*/
/*
============
Dimensions
============
*/
template<class U>
class Dimension
{
public:
typedef U UnitType;
//Member data
protected:
double m_value;
//Since I can't have by reference because I cannot convert to another unit
//a pointer must be used. The actual object has to be maintained in the
//subclass.
//ConvertTo() calls the pure virtual function SaveUnit()
UnitType *m_unit;
//Constructors
protected:
Dimension(double value, UnitType *unit)
: m_value(value), m_unit(unit)
{ }
//Destructor
public:
~Dimension()
{
DestroyUnit();
}
//Helpers
void DestroyUnit() { if (m_unit) delete m_unit; m_unit = NULL; }
void AssignUnit(UnitType *newUnit) { DestroyUnit(); m_unit = newUnit; }
double GetConversionFactor(UnitType const &newUnit) const
{
return m_unit->GetFactor() / newUnit.GetFactor();
}
//Public Interface
public:
double GetValue() const { return m_value; }
void SetValue(double newValue) { m_value = newValue; }
UnitType const& GetUnit() const { return m_unit; }
virtual void ConvertTo(UnitType const &newUnit)
{
m_value *= GetConversionFactor(newUnit);
AssignUnit(dynamic_cast<UnitType*>(newUnit.Clone()));
}
//Á la .Net:
virtual tstring ToString() const
{
tostringstream os;
os << m_value << _T(" ") << m_unit->GetSymbol();
return os.str();
}
virtual void ConvertToStandard()
{
ConvertTo(UnitType::StandardUnit);
}
//Operators
public:
virtual bool operator<(Dimension const &op2) const
{
return (m_value * GetConversionFactor(UnitType::StandardUnit))
< (op2.m_value * op2.GetConversionFactor(UnitType::StandardUnit));
}
virtual bool operator>(Dimension const &op2) const
{
return op2.operator <(*this);
}
virtual bool operator==(Dimension const &op2) const
{
return !(this->operator <(op2) || op2.operator <(*this));
}
};
template<class U>
tostream& operator<<(tostream &op1, Dimension<U> const &op2)
{
return (op1 << op2.ToString());
}
class Length : public Dimension<LengthUnit>
{
//Constructors
public:
Length() : Dimension<Length::UnitType>(0, dynamic_cast<Length::UnitType*>(Length::UnitType::StandardUnit.Clone()))
{ }
Length(double value, Length::UnitType const &unit) : Dimension<Length::UnitType>(value, dynamic_cast<Length::UnitType*>(unit.Clone()))
{ }
};
class Time : public Dimension<TimeUnit>
{
//Constructors
public:
Time() : Dimension<Time::UnitType>(0, dynamic_cast<Time::UnitType*>(Time::UnitType::StandardUnit.Clone()))
{ }
Time(double value, Time::UnitType const &unit) : Dimension<Time::UnitType>(value, dynamic_cast<Time::UnitType*>(unit.Clone()))
{ }
};
//class Speed : public Dimension<
/*
================
END Dimensions
================
*/
}
/*
===========
Shortcuts
===========
*/
#ifndef NO_UNIT_SHORTCUTS
#define Meter LIBNS::MeterUnit::Meter
#define Foot LIBNS::FootUnit::Foot
#endif // NO_UNIT_SHORTCUTS
#ifndef NO_PREFIX_SHORTCUTS
#define NoPrefix Prefix::NoPrefix
#define Deca LIBNS::Prefix::Deca
#define Hecto LIBNS::Prefix::Hecto
#define Kilo LIBNS::Prefix::Kilo
#define Mega LIBNS::Prefix::Mega
#define Giga LIBNS::Prefix::Giga
#define deci LIBNS::Prefix::deci
#define centi LIBNS::Prefix::centi
#define milli LIBNS::Prefix::milli
#define micro LIBNS::Prefix::micro
#define nano LIBNS::Prefix::nano
#endif // NO_PREFIX_SHORTCUTS
/*
===============
END Shortcuts
===============
*/
#include <iostream>
#ifdef _UNICODE
#define tcout std::wcout
#define tcin std::wcin
#else
#define tcout std::cout
#define tcin std::cin
#endif // _UNICODE
using std::endl;
int main()
{
Length myHeight(30.48, Meter);
Length doorHeight(100, Foot);
tcout << _T("I am ") << myHeight << _T(" tall.") << endl;
tcout << _T("The doorway is ") << doorHeight << _T(" high.") << endl;
myHeight.ConvertTo(Foot);
doorHeight.ConvertTo(Meter);
tcout << _T("I am ") << myHeight.ToString() << _T(" tall.") << endl;
tcout << _T("The doorway is ") << doorHeight.ToString() << _T(" high.") << endl;
if (myHeight > doorHeight)
tcout << _T("You cannot enter the room! You are too tall. :-(") << endl;
else if (myHeight < doorHeight)
tcout << _T("You may enter the room. You are not that tall.") << endl;
else
tcout << _T("Hmmmm... try to enter the room at your own risk! Your height and the door's height are the same.") << endl;
system("pause");
}