//Variant class implementation //Updated 6 Aug 2012 //WARNING: Untested code! Use at your own risk. //Found a bug? Awesome! Let us know via http://generaldevelopment.net/contact /*============================================================================== Copyright (c) 2011-2012 General Development Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==============================================================================*/ #include "Variant.hh" #include using namespace std; Variant::Variant() { type = VARIANT_NUL; } Variant::Variant(const Variant& s) { type = s.type; switch(type) { case VARIANT_NUL: break; case VARIANT_UND: break; case VARIANT_STR: data.s = new string(*(s.data.s)); break; case VARIANT_BOO: data.b = s.data.b; break; case VARIANT_INT: data.i = s.data.i; break; case VARIANT_FLO: data.d = new double(); *(data.d) = *(s.data.d); break; }; } Variant::Variant(const string& s) { type = VARIANT_STR; data.s = new string(s); } Variant::Variant(const char& c) { type = VARIANT_STR; data.s = new string(); *(data.s) = c; } Variant::Variant(const bool& b) { type = VARIANT_BOO; data.b = b; } Variant::Variant(const int& i) { type = VARIANT_INT; data.i = i; } Variant::Variant(const double& f) { type = VARIANT_FLO; data.d = new double(); *(data.d) = f; } Variant::~Variant() { clear(); } Variant::operator string() const throw(Undefined) { stringstream out; switch(type) { case VARIANT_NUL: return ""; case VARIANT_UND: throw Undefined(); case VARIANT_STR: return *(data.s); case VARIANT_BOO: return data.b ? "true" : "false"; case VARIANT_INT: out << data.i; return out.str(); case VARIANT_FLO: out << *(data.d); return out.str(); } return ""; //keep compiler happy. Never reached. } Variant::operator bool() const throw(Undefined) { switch(type) { case VARIANT_NUL: return false; case VARIANT_UND: throw Undefined(); case VARIANT_INT: return (bool)data.i; case VARIANT_FLO: return (bool)(*(data.d)); case VARIANT_BOO: return data.b; case VARIANT_STR: return data.s->size() != 0; } return false; //keep compiler happy } Variant::operator int() const throw(Undefined) { switch(type) { case VARIANT_INT: return data.i; case VARIANT_FLO: return (int) *(data.d); case VARIANT_BOO: return data.b; case VARIANT_STR: throw Undefined(); case VARIANT_UND: throw Undefined(); case VARIANT_NUL: default: return 0; } } Variant::operator float() const throw(Undefined) { //Variant has no inherent single-precision float storage, so convert via double return (float)(double)(*this); } Variant::operator double() const throw(Undefined) { switch(type) { case VARIANT_INT: return (double)data.i; case VARIANT_FLO: return *(data.d); case VARIANT_BOO: return data.b; case VARIANT_STR: throw Undefined(); case VARIANT_UND: throw Undefined(); case VARIANT_NUL: default: return 0.; } } size_t Variant::size() { switch(type) { case VARIANT_STR: return data.s->size(); case VARIANT_NUL: return 0; default: return 1; } } void Variant::clear() { if(type == VARIANT_STR) delete data.s; else if(type == VARIANT_FLO) delete data.d; type = VARIANT_NUL; return; } void Variant::setUnd() { clear(); type = VARIANT_UND; } Variant& Variant::operator=(const Variant& s) { if(&s == this) return *this; //avoid possible stuff-up from self-assignemnt clear(); type = s.type; switch(type) { case VARIANT_NUL: break; case VARIANT_UND: break; case VARIANT_INT: data.i = s.data.i; break; case VARIANT_FLO: data.d = new double(); *(data.d) = *(s.data.d); break; case VARIANT_BOO: data.b = s.data.b; break; case VARIANT_STR: data.s = new string(*(s.data.s)); break; }; return *this; } Variant& Variant::operator=(const string& s) { clear(); type = VARIANT_STR; data.s = new string(s); return *this; } Variant& Variant::operator=(const char& c) { clear(); type = VARIANT_STR; data.s = new string(); *(data.s) = c; return *this; } Variant& Variant::operator=(const bool& b) { clear(); type = VARIANT_BOO; data.b = b; return *this; } Variant& Variant::operator=(const int& i) { clear(); type = VARIANT_INT; data.i = i; return *this; } Variant& Variant::operator=(const double& d) { clear(); type = VARIANT_FLO; data.d = new double(); *(data.d) = d; return *this; } Variant& Variant::operator+=(const Variant& b) { if(type == VARIANT_NUL) //if one value is nul then the result is the other value *this = b; else if(b.type == VARIANT_NUL) ; //nothing to be done! else if(type == VARIANT_UND) ; //nothing to be done else if(b.type == VARIANT_UND) setUnd(); else if(type == VARIANT_STR || b.type == VARIANT_STR) //if either operand is string, promote both to strings and concatenate *this = ((string)(*this)) + ((string)b); else if(type == VARIANT_BOO || b.type == VARIANT_BOO) //if either operand is boolean, result is undefined: setUnd(); else if(type == VARIANT_INT && b.type == VARIANT_INT) //if both operands are int, perform integer additon: *this = ((int)(*this)) + ((int)b); else //if one or more floats, perform floating point addition: *this = ((double)(*this)) + (double(b)); return *this; } Variant& Variant::operator+=(const string& s) { Variant b = s; return (*this)+=b; } Variant& Variant::operator+=(const char& c) { Variant b = c; return (*this)+=b; } Variant& Variant::operator+=(const int& i) { Variant b = i; return (*this)+=b; } Variant& Variant::operator+=(const double& f) { Variant b = f; return (*this)+=b; } Variant& Variant::operator-=(const Variant& b) { if(type==VARIANT_NUL) //if first operand is null, negate second operand as result *this = -b; else if(b.type==VARIANT_NUL) //if second operand is null, do nothing ; //do not remove this as else statements follow else if(type==VARIANT_UND) ; //do nothing else if(b.type==VARIANT_UND) setUnd(); else if(b.type==VARIANT_STR) //if second operand is a string, result is undefined setUnd(); else if(type==VARIANT_STR) //simlarly if first operand is string... setUnd(); else if(type==VARIANT_BOO) //subtraction of/from boolean is undefined setUnd(); else if(b.type==VARIANT_BOO) setUnd(); else *this = *this + (-b); return *this; } Variant& Variant::operator-=(const int& i) { Variant b = i; return (*this)-=b; } Variant& Variant::operator-=(const double& f) { Variant b = f; return (*this)-=f; } Variant& Variant::operator*=(const Variant& b) { if(type == VARIANT_NUL || b.type == VARIANT_NUL) //if either operand is nul, result is null... clear(); else if(type == VARIANT_UND || b.type == VARIANT_UND) //if either operand is undefined, result is undefined. setUnd(); else if(type == VARIANT_STR || b.type == VARIANT_STR) //if either operand is string, result is undefined: setUnd(); else if(type == VARIANT_BOO || b.type == VARIANT_BOO) //if either operand is bool, result undefined: setUnd(); else if(type == VARIANT_INT && b.type == VARIANT_INT) //if both operands are int, do integer multiplication: *this = ((int)(*this)) * ((int)b); else //otherwise do floating point multiplication... *this = ((double)(*this)) * ((double)b); return *this; } Variant& Variant::operator*=(const int& i) { Variant b = i; return (*this)*=b; } Variant& Variant::operator*=(const double& f) { Variant b = f; return (*this)*=b; } Variant& Variant::operator/=(const Variant& b) { //behavior differs from C++ in that integer division with nonzero remainder produces flo result if(type == VARIANT_NUL) //if first operand is nul, result is null... clear(); else if(type == VARIANT_STR || b.type == VARIANT_STR || type == VARIANT_BOO || b.type == VARIANT_BOO || b.type == VARIANT_NUL || type == VARIANT_UND || b.type == VARIANT_UND) //if either operand is string, either operand is bool, or second operand is nul, result is undefined: setUnd(); else if(type == VARIANT_INT && b.type == VARIANT_INT && (((int)(*this)) % ((int)b) == 0)) //if both operands are int, and remainder is 0, do integer division *this = ((int)(*this)) / ((int)b); else //otherwise do floating point division... *this = ((double)(*this)) / ((double)b); return *this; } Variant& Variant::operator/=(const int& i) { Variant b = i; return (*this) /= b; } Variant& Variant::operator/=(const double& f) { Variant b = f; return (*this) /= b; } Variant Variant::operator-() const throw(Undefined) { Variant b; switch(type) { case VARIANT_NUL: //nothing to do break; case VARIANT_STR: throw Undefined(); break; case VARIANT_BOO: throw Undefined(); break; case VARIANT_INT: b = -(int)(*this); break; case VARIANT_FLO: b = -(double)(*this); break; }; return b; } Variant Variant::operator+(const Variant& b) const { Variant result = *this; result += b; return result; } Variant Variant::operator+(const std::string& s) const { Variant result = *this; result += s; return result; } Variant Variant::operator+(const char& c) const { Variant result = *this; result += c; return result; } Variant Variant::operator+(const int& i) const { Variant result = *this; result += i; return result; } Variant Variant::operator+(const double& d) const { Variant result = *this; result += d; return result; } Variant Variant::operator-(const Variant& b) const { Variant result = *this; result -= b; return result; } Variant Variant::operator-(const int& i) const { Variant result = *this; result -= i; return result; } Variant Variant::operator-(const double& d) const { Variant result = *this; result -= d; return result; } Variant Variant::operator*(const Variant& b) const { Variant result = *this; result *= b; return result; } Variant Variant::operator*(const int& i) const { Variant result = *this; result *= i; return result; } Variant Variant::operator*(const double& d) const { Variant result = *this; result *= d; return result; } Variant Variant::operator/(const Variant& b) const { Variant result = *this; result /= b; return result; } Variant Variant::operator/(const int& i) const { Variant result = *this; result /= i; return result; } Variant Variant::operator/(const double& d) const { Variant result = *this; result /= d; return result; } int Variant::compare(const Variant& b) const throw(Undefined) { //returns <0 if this0 if this>b if(this == &b) return 0; //same instance if(type == VARIANT_STR && b.type == VARIANT_STR) //if both operands are of type string, perform string comparison return data.s->compare(*(b.data.s)); else if(type == VARIANT_STR || b.type == VARIANT_STR) //if only one operand is a string, the comparison is invalid throw Undefined(); else if(type == VARIANT_UND || b.type == VARIANT_UND) throw Undefined(); if(type == VARIANT_BOO || b.type == VARIANT_BOO || type == VARIANT_NUL || b.type == VARIANT_NUL) { //if either operand is of type boo, translate the other to boolean for comparison... //also, if either operand is null, null behaves like boolean false if((bool)(*this) == (bool)b) return 0; //true == true or false == false else if((bool)(*this)) return 1; //true > false else return -1; //false < true } //remaining cases are: 1 or 2 int operands, 1 or 2 flo operands, 1 string operand if(type == VARIANT_FLO || b.type == VARIANT_FLO) //if a flo is present, promote other operand to flo... return ((double)(*this)) - ((double)b); //that leaves the case of 2 int operands... return ((int)(*this)) - ((int)b); //all cases done! } bool Variant::operator==(const Variant& b) const throw(Undefined) { return compare(b)==0; } bool Variant::operator!=(const Variant& b) const throw(Undefined) { return compare(b)!=0; } bool Variant::operator>(const Variant& b) const throw(Undefined) { return compare(b)>0; } bool Variant::operator>=(const Variant& b) const throw(Undefined) { return compare(b)>=0; } bool Variant::operator<(const Variant& b) const throw(Undefined) { return compare(b)<0; } bool Variant::operator<=(const Variant& b) const throw(Undefined) { return compare(b)<=0; }