/** \file * Currency wrapper. */ /* Copyright © 2001 Michael Geddes, Sofus Mortensen * * This material is provided "as is", with absolutely no warranty * expressed or implied. Any use is at your own risk. Permission to * use or copy this software for any purpose is hereby granted without * fee, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is * granted, provided the above notices are retained, and a notice that * the code was modified is included with the above copyright notice. * * This header is part of Comet version 2. * https://github.com/alamaison/comet */ #ifndef COMET_CURRENCY_H #define COMET_CURRENCY_H #include #include #include #include #include #include namespace comet { class bstr_t; // currency_t /////////////// /*! \addtogroup COMType */ //@{ /** Wrapper for CURRENCY type. * CURRENCY is a fixed point (to 4 decimal places) 64 bit value. */ class currency_t { public: /// Default Constructor currency_t() throw() { cy_.int64 = 0; } /// CY constructor. currency_t(const CY &cy): cy_(cy) { } /// Double Conversion constructor. explicit currency_t(double val) { VarCyFromR8(val,&cy_) | raise_exception; } #if 0 // //! Construct currency from CY/CURRENCY. // /*! // Takes ownership of specified CY value. To prevent misuse the CY must be wrapped using auto_attach. // // \code // currency_t cy( auto_attach( myCY ) ); // \endcode // // \param cyVal // Value to initialise currency_t from. // */ // currency_t( const impl::auto_attach_t &cyVal) throw() : cy_(cyVal.get()) // {} #endif //0 /// Long Conversion constructor. currency_t( long val ) { VarCyFromI4(val,&cy_) | raise_exception; } currency_t( int val ) { VarCyFromI4(val,&cy_) | raise_exception; } void swap(currency_t& c) throw() { std::swap(cy_, c.cy_); } static const currency_t& create_const_reference(const CY& s) throw() { return *reinterpret_cast(&s); } static currency_t& create_reference(CY& s) throw() { return *reinterpret_cast(&s); } //! \name Assignment Operators //@{ currency_t &operator=(double newVal) { currency_t c( newVal ); swap(c); return *this; } currency_t &operator=(long newVal) { currency_t c( newVal ); swap(c); return *this; } currency_t &operator=(int newVal) { currency_t c( newVal ); swap(c); return *this; } currency_t &operator=(const tagCY &newVal) throw() { cy_ = newVal; return *this; } //@} /// \name Mathematical Operators //@{ currency_t &operator+=(const currency_t &cy) { currency_t c( *this + cy ); swap(c); return *this; } currency_t &operator-=(const currency_t &cy) { currency_t c( *this - cy ); swap(c); return *this; } currency_t &operator*=(const currency_t &cy) { currency_t c( *this * cy ); swap(c); return *this; } currency_t &operator*=(long cy) { currency_t c( *this * cy ); swap(c); return *this; } currency_t &operator*=(int val) { return operator*=((long)val); } currency_t &operator*=(double val) { cy_.int64 = (LONGLONG)(cy_.int64 * val); return *this; } currency_t &operator/=(int val) const { return operator/=((long)val); } currency_t &operator/=(long val) { if(!valid()) { // Check for invalid number return *this; } // Check for divide by 0 if (val == 0) { // Set to maximum negative value cy_.Hi = 0x80000000; cy_.Lo = 0x00000000; return *this; } cy_.int64/=val; return *this; } currency_t operator+(const currency_t &cy)const { currency_t rv; VarCyAdd(cy_,cy.cy_,&rv.cy_) | raise_exception ; return rv; } currency_t operator-(const currency_t &cy)const { currency_t rv; VarCySub(cy_,cy.cy_,&rv.cy_) | raise_exception ; return rv; } currency_t operator*(const currency_t &cy)const { currency_t rv; VarCyMul(cy_,cy.cy_,&rv.cy_) | raise_exception ; return rv; } currency_t operator*(long cy)const { currency_t rv; VarCyMulI4(cy_,cy,&rv.cy_) | raise_exception; return rv; } currency_t operator*(int cy)const { return operator *((long)cy); } currency_t operator*(double cy) const { currency_t val(*this); val *=cy; return val; } /// Calculate approximate ratio. double operator/(const currency_t &cy)const { return ((double)cy_.int64 /(double)cy.cy_.int64); } /// Divide by int. currency_t operator/(int val) const { return operator/((long)val); } /// Divide by long. currency_t operator/(long val) const { currency_t tmp(cy_); tmp/=val; return tmp; } /// Divide by double double operator/(double val) const { if(!valid()) { // Check for invalid number throw std::invalid_argument("Invalid divide"); } // Check for divide by 0 if (val == 0) { throw std::overflow_error("Divide by 0"); } return cy_.int64/(val*10000); } /// Unary negate. currency_t operator-()const { currency_t cy; VarCyNeg(cy_,&(cy.cy_)) | raise_exception; return cy; } //@} /** Rounds the value to specified number of decimal places. * \param decimals Number of places to round to. */ currency_t &round_to(int decimals) { VarCyRound(cy_,decimals,&cy_) | raise_exception; return *this; } /// \name Logical Operators //@{ bool operator!=(const currency_t &cy) const { return cmp(cy)!=0; } bool operator!=(double val) const{ return cmp(val)!=0; } bool operator==(const currency_t &cy) const { return cmp(cy)==0; } bool operator==(double val) const{ return cmp(val)==0; } bool operator<=(const currency_t &cy) const{ return cmp(cy)<=0; } bool operator<=(double val) const{ return cmp(val)<=0; } bool operator>=(const currency_t &cy) const{ return cmp(cy)>=0; } bool operator>=(double val) const{ return cmp(val)>=0; } bool operator<(const currency_t &cy) const{ return cmp(cy)<0; } bool operator<(double val) const{ return cmp(val)<0; } bool operator>(const currency_t &cy) const{ return cmp(cy)>0; } bool operator>(double val) const{ return cmp(val)>0; } //@} /** Compares the value like strcmp. * \param cy Number to be compared. */ int cmp(const currency_t &cy) const { return _cmpResult(VarCyCmp(cy_,cy.cy_)); } /** Compares the value like strcmp. * \param cy Number to be compared. */ int cmp(double cy) const { return _cmpResult(VarCyCmpR8(cy_,cy)); } //! \name Access converters //@{ tagCY get() const { return cy_;} tagCY in() const { return cy_;} tagCY *in_ptr() const { return const_cast(&cy_);} tagCY *out() { return &cy_;} tagCY *inout() { return &cy_;} //@} #if 0 friend std::ostream &operator <<(std::ostream &str, const currency_t &val) { std::string strval=val.format( 1, str.precision(), str.width() ); return str<< strval.c_str(); } #endif friend std::basic_ostream &operator<<(std::basic_ostream &str, const currency_t &val) { std::basic_string strval; val.do_format(strval, 1, str.precision(), str.width() ); return str << strval.c_str(); } friend std::basic_ostream &operator<<(std::basic_ostream &str, const currency_t &val) { std::basic_string strval; val.do_format(strval, 1, str.precision(), str.width() ); return str << strval.c_str(); } //! Format the string with the given digits, precision and width. std::basic_string format( std::streamsize mindigits=0, std::streamsize minprecision=0, std::streamsize width=0) const { std::basic_string strval; do_format(strval, mindigits, minprecision, width); return strval; } //! Parse the string to a currency. currency_t &parse( const bstr_t &str, LCID locale =::GetThreadLocale() ); /* { VarCyFromStr( str.in(), locale, 0, &cy_ ) | raise_exception; return *this; }*/ protected: /** Return a string representation of the value. * \param val output string (return values can't automatically detect template arguments) * \param mindigits Minimum number before decimal point. * \param minprecision Minimum number after decimal point. * \todo Obey ostream formats for: fillchar(), ios_base::left, ios_base::internal, ios_base::showpos */ template void do_format( std::basic_string& val, std::streamsize mindigits, std::streamsize minprecision, std::streamsize /*width*/) const { COMET_ASSERT(mindigits>=0 && minprecision >=0 ); if(minprecision> 4) minprecision =4; // Add in the 4 fixed decimal points std::streamsize pr = ((0 <= minprecision && minprecision <=4) ? (4-minprecision) : 0); mindigits+=4; val.erase(); val.reserve(22); LONGLONG value=cy_.int64; bool neg=value<0; if(neg) { value=-value; } // Put in the digits backwards std::streamsize digit=0; bool output=false; while(value !=0 || digit < mindigits) { CH dig=CH(value%10); if(output || true==(output= (dig >0 || digit>=pr))) // Once 'output' is set - output everything. { val+=(CH('0'+dig)); } if(++digit == 4) { val+=CH('.'); output=true; } value/=10; } if(neg) { val+=CH('-'); } // Now reverse the digits std::reverse(val.begin(), val.end()); } public: /// Returns true if this is a valid number bool valid() const throw() { return !(cy_.Hi==(long)0x80000000 && cy_.Lo==0);} /// Detaches the CY value. Provided for consistancy. CY detach() throw() { CY val = cy_; cy_.int64 = 0; return val; } /// Detaches the CY value. Provided for consistancy. static CY detach(currency_t& cy) throw() { return cy.detach(); } private: CY cy_; static int _cmpResult(HRESULT hr) { if(SUCCEEDED(hr)) { switch(hr) { case VARCMP_LT: return -1; case VARCMP_EQ : return 0; case VARCMP_GT: return 1; case VARCMP_NULL: COMET_ASSERT(!"What do we do with this?"); } } else { hr | raise_exception; } return 0; // shut the compiler up } }; //@} } #endif // COMET_CURRENCY_H