// $Id: AnyScalar.h 59 2007-07-17 14:43:23Z tb $ /* * STX Expression Parser C++ Framework v0.7 * Copyright (C) 2007 Timo Bingmann * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; either version 2.1 of the License, or (at your * option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** \file AnyScalar.h * Definition of a typed scalar value class AnyScalar used by the parser to * represent values. */ #ifndef _STX_AnyScalar_H_ #define _STX_AnyScalar_H_ #include <string> #include <stdexcept> #include <functional> #include <ostream> #include <assert.h> namespace stx { /** AnyScalar constructs objects holding a typed scalar value. It supports * boolean values, integer values both signed and unsigned, floating point * values and strings. The class provides operators which will compare scalars * between other scalars by converting them into a common domain. Furthermore * arithmetic operator will compose one or two scalars where the calculation is * done in the "higher" domain. */ class AnyScalar { public: /// Enumeration establishing identifiers for all supported types. All /// "small" integer types are included, because the class use originally /// designed for serializing large amounts of data. enum attrtype_t { /// Flag identifier for an uninitialized object. ATTRTYPE_INVALID = 0x00, /// A boolean type holding only true and false. ATTRTYPE_BOOL = 0x01, /// Character type, not viewed as a letter or digit, but as 8 bit /// signed integer. ATTRTYPE_CHAR = 0x10, /// Short signed integer type, 2 bytes long. ATTRTYPE_SHORT = 0x11, /// Medium signed integer type, 4 bytes long. ATTRTYPE_INTEGER = 0x12, /// Long (long) signed integer type, 8 bytes long. ATTRTYPE_LONG = 0x13, /// Byte type, not viewed as a letter or digit, but as 8 bit unsigned /// integer. ATTRTYPE_BYTE = 0x20, /// Short unsigned integer type, 2 bytes long. ATTRTYPE_WORD = 0x21, /// Medium unsigned integer type, 4 bytes long. ATTRTYPE_DWORD = 0x22, /// Long (long) unsigned integer type, 8 bytes long. ATTRTYPE_QWORD = 0x23, /// Single precision floating point type, 4 bytes long. ATTRTYPE_FLOAT = 0x30, /// Double precision floating point type, 8 bytes long. ATTRTYPE_DOUBLE = 0x31, /// String type, variable length. Needs much extra care to handle /// memory allocation. ATTRTYPE_STRING = 0x40 }; private: /// The currently set type in the union. attrtype_t atype; /// Union type to holding the current value of an AnyScalar. union value_t { /// Used for ATTRTYPE_BOOL, ATTRTYPE_CHAR, ATTRTYPE_SHORT, ATTRTYPE_INTEGER int _int; /// Used for ATTRTYPE_BYTE, ATTRTYPE_WORD, ATTRTYPE_DWORD unsigned int _uint; /// Used for ATTRTYPE_LONG long long _long; /// Used for ATTRTYPE_QWORD unsigned long long _ulong; /// Used for ATTRTYPE_FLOAT float _float; /// Used for ATTRTYPE_DOUBLE double _double; /// Used for ATTRTYPE_STRING, make sure it get delete'ed correctly. std::string* _string; }; /// Union holding the current value of set type. union value_t val; public: /// Create a new empty AnyScalar object of given type. explicit inline AnyScalar(attrtype_t t = ATTRTYPE_INVALID) : atype(t) { if (atype == ATTRTYPE_STRING) { val._string = new std::string; } else { val._ulong = 0; } } // *** Constructors for the various types /// Construct a new AnyScalar object of type ATTRTYPE_BOOL and set the given /// boolean value. inline AnyScalar(bool b) : atype(ATTRTYPE_BOOL) { val._int = b; } #ifndef SWIG // This constructor confuses SWIG into calling it with // one-character strings /// Construct a new AnyScalar object of type ATTRTYPE_CHAR and set the given /// char value. inline AnyScalar(char c) : atype(ATTRTYPE_CHAR) { val._int = c; } #endif /// Construct a new AnyScalar object of type ATTRTYPE_SHORT and set the given /// short value. inline AnyScalar(short s) : atype(ATTRTYPE_SHORT) { val._int = s; } /// Construct a new AnyScalar object of type ATTRTYPE_INTEGER and set the /// given integer value. inline AnyScalar(int i) : atype(ATTRTYPE_INTEGER) { val._int = i; } /// Construct a new AnyScalar object of type ATTRTYPE_INTEGER and set the /// given integer value. inline AnyScalar(long i) : atype(ATTRTYPE_INTEGER) { val._int = i; } /// Construct a new AnyScalar object of type ATTRTYPE_LONG and set the /// given long value. inline AnyScalar(long long l) : atype(ATTRTYPE_LONG) { val._long = l; } /// Construct a new AnyScalar object of type ATTRTYPE_BYTE and set the given /// unsigned value. inline AnyScalar(unsigned char c) : atype(ATTRTYPE_BYTE) { val._uint = c; } /// Construct a new AnyScalar object of type ATTRTYPE_WORD and set the given /// unsigned value. inline AnyScalar(unsigned short s) : atype(ATTRTYPE_WORD) { val._uint = s; } /// Construct a new AnyScalar object of type ATTRTYPE_DWORD and set the given /// unsigned value. inline AnyScalar(unsigned int i) : atype(ATTRTYPE_DWORD) { val._uint = i; } /// Construct a new AnyScalar object of type ATTRTYPE_DWORD and set the given /// unsigned value. inline AnyScalar(unsigned long i) : atype(ATTRTYPE_DWORD) { val._uint = i; } /// Construct a new AnyScalar object of type ATTRTYPE_QWORD and set the given /// unsigned value. inline AnyScalar(unsigned long long l) : atype(ATTRTYPE_QWORD) { val._ulong = l; } /// Construct a new AnyScalar object of type ATTRTYPE_FLOAT and set the given /// floating point value. inline AnyScalar(float f) : atype(ATTRTYPE_FLOAT) { val._float = f; } /// Construct a new AnyScalar object of type ATTRTYPE_DOUBLE and set the /// given floating point value. inline AnyScalar(double d) : atype(ATTRTYPE_DOUBLE) { val._double = d; } /// Construct a new AnyScalar object of type ATTRTYPE_STRING and set the /// given string value. inline AnyScalar(const char *s) : atype(ATTRTYPE_STRING) { if (s == NULL) val._string = new std::string; else val._string = new std::string(s); } /// Construct a new AnyScalar object of type ATTRTYPE_STRING and set the /// given string value. inline AnyScalar(const std::string &s) : atype(ATTRTYPE_STRING) { val._string = new std::string(s); } /// Destroy the object: free associated string memory if necessary. inline ~AnyScalar() { if (atype == ATTRTYPE_STRING) { delete val._string; val._string = NULL; } } /// Copy-constructor to deal with enclosed strings. Transfers type and /// value. inline AnyScalar(const AnyScalar &a) : atype(a.atype) { switch(atype) { case ATTRTYPE_INVALID: break; case ATTRTYPE_BOOL: case ATTRTYPE_CHAR: case ATTRTYPE_SHORT: case ATTRTYPE_INTEGER: val._int = a.val._int; break; case ATTRTYPE_BYTE: case ATTRTYPE_WORD: case ATTRTYPE_DWORD: val._uint = a.val._uint; break; case ATTRTYPE_LONG: val._long = a.val._long; break; case ATTRTYPE_QWORD: val._ulong = a.val._ulong; break; case ATTRTYPE_FLOAT: val._float = a.val._float; break; case ATTRTYPE_DOUBLE: val._double = a.val._double; break; case ATTRTYPE_STRING: val._string = new std::string(*a.val._string); break; } } /// Assignment operator to deal with enclosed strings. Transfers type and /// value. inline AnyScalar& operator=(const AnyScalar &a) { // check if we are to assign ourself if (this == &a) return *this; if (atype == ATTRTYPE_STRING) { delete val._string; val._string = NULL; } atype = a.atype; switch(atype) { case ATTRTYPE_INVALID: assert(0); break; case ATTRTYPE_BOOL: case ATTRTYPE_CHAR: case ATTRTYPE_SHORT: case ATTRTYPE_INTEGER: val._int = a.val._int; break; case ATTRTYPE_BYTE: case ATTRTYPE_WORD: case ATTRTYPE_DWORD: val._uint = a.val._uint; break; case ATTRTYPE_LONG: val._long = a.val._long; break; case ATTRTYPE_QWORD: val._ulong = a.val._ulong; break; case ATTRTYPE_FLOAT: val._float = a.val._float; break; case ATTRTYPE_DOUBLE: val._double = a.val._double; break; case ATTRTYPE_STRING: val._string = new std::string(*a.val._string); break; } return *this; } /// Comparison operator. Directly compares type _and_ value. Does NOT /// attempt to convert the values into a common domain. bool operator==(const AnyScalar &a) const; /// Comparison operator: Directly compares type _and_ value. Does NOT /// attempt to convert the values into a common domain. inline bool operator!=(const AnyScalar &a) const { return !(*this == a); } /// Return the type identifier of the object. inline attrtype_t getType() const { return atype; } /// Returns true if this object contains a boolean value. inline bool isBooleanType() const { return (atype == ATTRTYPE_BOOL); } /// Returns true if this object contains one of the integer types. inline bool isIntegerType() const { return (atype == ATTRTYPE_BOOL || atype == ATTRTYPE_CHAR || atype == ATTRTYPE_SHORT || atype == ATTRTYPE_INTEGER || atype == ATTRTYPE_LONG || atype == ATTRTYPE_BYTE || atype == ATTRTYPE_WORD || atype == ATTRTYPE_DWORD || atype == ATTRTYPE_QWORD); } /// Returns true if this object contains one of the floating point types. inline bool isFloatingType() const { return (atype == ATTRTYPE_FLOAT || atype == ATTRTYPE_DOUBLE); } /// Attempts to convert the current type/value into the given type. Returns /// false if the value could not be represented in the new type, the new /// type is set nonetheless. See the getXXX below on how the new value is /// computed from the old type. bool convertType(attrtype_t t); /// Changes the current type and resets the contents without attempting to /// convert the enclosed value. void resetType(attrtype_t t); // *** attrtype_t to string and string to attrtype_t functions /// Returns true if the attrtype_t is a valid type identifier. static bool isValidAttrtype(attrtype_t at); /// Returns the attrtype identifier of a string, throws ConversionException /// if s does not specify a valid type name. static attrtype_t stringToType(const std::string &s) { return stringToType(s.c_str()); } /// Returns the attrtype identifier a string, throws ConversionException if /// it does not specify a valid type name. static attrtype_t stringToType(const char *s); /// Returns a const char* for each attrtype_t. static std::string getTypeString(attrtype_t at); /// Returns a string for this AnyScalar's type. inline std::string getTypeString() const { return getTypeString(atype); } // *** Type and Value Length Functions /// Return the storage length of the type in bytes. Beware that /// getTypeLength(bool) == 0. inline int getTypeLength() const { return getTypeLength(atype); } /// Return the storage length of the type in bytes. Beware that /// getTypeLength(bool) == 0. static int getTypeLength(attrtype_t t); /// Boolean check if this type is of fixed length. static bool isFixedLength(attrtype_t t) { return getTypeLength(t) >= 0; } /// Boolean check if this type is of fixed length. inline bool isFixedLength() const { return isFixedLength(atype); } /// Return the storage length of this value (mark the different to /// getTypeLength()) unsigned int getValueLength() const; // *** Setters // Sets the value, converting the input to the currently set type if // necessary. Returns false if the input could not be converted into the // right type. /** Change the value of the current object to i. Converting the integer * to this object's type if necessary. * * - boolean: the integer i is cut to 0 or 1. * - char: cut to -128..127 returns false if out of range. * - short: cut to -32768..32767 return false if out of range. * - byte: cut to 0..255 returns false if out of range. * - word: cut to 0..65536 returns false if out of range. * - int: no conversion. * - dword: cut to 0..4294967295 return false if it was negative. * - long: no cutting necessary. * - qword: cut to positive number. * - float: integer stored as floating point * - double: integer stored as floating point * - string: integer is stringified. */ bool setInteger(int i); /** Change the value of the current object to l. Converting the long * long integer to this object's type if necessary. If the long long does * not fully fit into the current type, then this function returns false. * * - boolean: the long i is cut to 0 or 1. * - char: cut to -128..127 returns false if out of range. * - short: cut to -32768..32767 return false if out of range. * - int: cut to -2147483646..2147483647 returns false if out of range. * - byte: cut to 0..255 returns false if out of range. * - word: cut to 0..65536 returns false if out of range. * - dword: cut to 0..4294967295 return false if out of range. * - long: no conversion * - qword: cut to positive number. * - float: long stored as floating point * - double: long stored as floating point * - string: long is stringified. */ bool setLong(long long l); /** Change the value of the current object to d. Converting the floating * point to this object's type if necessary. * * - boolean: [0:0.5] is false, (0.5:1] is true. else true and return false. * - char: truncated to integer ranged -128..127 returns false if out of range. * - short: truncated to integer ranged -32768..32767 return false if out of range. * - int: truncated to integer ranged -2147483646..2147483647 returns false if out of range. * - byte: truncated to integer ranged 0..255 returns false if out of range. * - word: truncated to integer ranged 0..65536 returns false if out of range. * - dword: truncated to integer ranged 0..4294967295 return false if out of range. * - long: truncated to integer ranged -9223372036854775806..9223372036854775807 * - qword: truncated to integer ranged 0..18446744073709551615 * - float: truncated to single-precision * - double: no conversion * - string: double is stringified. */ bool setDouble(double d); /** Change the value of the current object to s. Converting the string * to this object's type if necessary. If this string cannot be converted * to say an integer, then this function returns false. * * - boolean: "0", "f", "false", "n" and "no" are false, * "1", "t", "true", "y" and "yes" are true. else returns false. * - char: string read using strtol and truncated to -128..127 returns false if out of range or unparseable. * - short: string read using strtol and truncated to -32768..32767 returns false if out of range or unparseable. * - int: string read using strtol returns false if out of range or unparseable. * - long: string read using strtoll returns false if out of range or unparseable. * - byte: string read using strtoul and truncated to 0..255 returns false if out of range or unparseable. * - word: string read using strtoul and truncated to 0..65535 returns false if out of range or unparseable. * - dword: string read using strtoul returns false if out of range or unparseable. * - qword: string read using strtoull returns false if out of range or unparseable. * - float: string read using strtod and converted to single-precision returns false if unparseable. * - double: string read using strtod returns false if unparseable. * - string: no conversion */ bool setString(const std::string &s); /// Change the value of the current object to s. This string s must be /// quoted: "str". This function resolves escape sequences like \n. bool setStringQuoted(const std::string &s); /** Change the type _and_ value of the current object to s. The current * type is set if the given string can be converted to an integer or a * floating point number. This was required because e.g. the usual input * from files or perl are all untyped strings. The following input * conversions are tested and yield the given types: * * - boolean: no boolean strings are matched! * - int: input was readable by strtoll and is small enough. * - long: input was readable by strtoll * - double: input was readble by strtod * - string: all above failed. * * @return reference to this for chaining. */ AnyScalar& setAutoString(const std::string &input); // *** Getters // Return the enclosed value in different types, converting if // necessary. If the enclosed value cannot be converted these functions // throw a ConversionError exception. /** Return the value converted to a boolean. If the enclosed value could * not be converted this function throws a ConversionException. * * - boolean: no conversion * - char/short/int/long: test integer != 0 * - byte/word/dword/qword: test integer != 0 * - float/double: test float != 0.0 * - string: "0", "f", "false", "n", "no" are false, * "1", "t", "true", "y", "yes" are true * other strings throw ConversionException. */ bool getBoolean() const; /** Return the value converted to an integer. If the enclosed value could * not be converted this function throws a ConversionException. * * - boolean: returns 0 or 1. * - char/short/int: return the integer * - byte/word/dword: return the integer (possibly as a negative number) * - long: cut to -2147483646..2147483647. * - qword: cut to 0..4294967295 * - float/double: machine-conversion floating point to integer * - string: Attempt to strtol the string, throws ConversionException if this fails. */ int getInteger() const; /** Return the value converted to an unsigned integer. If the enclosed * value could not be converted this function throws a ConversionException. * * - boolean: returns 0 or 1. * - char/short/int: return the integer (possibly representing a negative number as positive) * - byte/word/dword: return the unsigned integer * - long: cut to 0..4294967295 (also misrepresenting because of machine-conversion) * - qword: cut to 0..4294967295 * - float/double: machine-conversion floating point to unsigned integer. * - string: Attempt to strtoul the string, throws ConversionException if this fails. */ unsigned int getUnsignedInteger() const; /// Alias for getInteger() inline int getInt() const { return getInteger(); } /// Alias for getUnsignedInteger() inline unsigned int getUInt() const { return getUnsignedInteger(); } /** Return the value converted to a long integer. If the enclosed value * could not be converted this function throws a ConversionException. * * - boolean: returns 0 or 1. * - char/short/int: return the integer (possibly representing a negative number as positive) * - byte/word/dword: return the unsigned integer * - long: no conversion * - qword: return the integer (possibly converting a large long to a negative) * - float/double: machine-conversion floating point to long long. * - string: Attempt to strtoll the string, throws ConversionException if this fails. */ long long getLong() const; /** Return the value converted to an unsigned long integer. If the enclosed * value could not be converted this function throws a ConversionException. * * - boolean: returns 0 or 1. * - char/short/int: return the integer (possibly representing a negative number as positive) * - byte/word/dword: return the unsigned integer * - long: return the long (possibly interpreting a negative long as a large positive) * - qword: no conversion * - float/double: machine-conversion floating point to unsigned long long. * - string: Attempt to strtoull the string, throws ConversionException if this fails. */ unsigned long long getUnsignedLong() const; /// Alias for getUnsignedLong() unsigned long long getULong() const { return getUnsignedLong(); } /** Return the value converted to a double. If the enclosed value could not * be converted this function throws a ConversionException. * * - boolean: returns 0 or 1.0. * - char/short/int/long: return the integer as a float * - byte/word/dword/qword: return the unsigned integer as a float * - float: cast to double. * - double: no conversion * - string: Attempt to strtod the string, throws ConversionException if this fails. */ double getDouble() const; /** Return the value converted to a string. This function never throws * ConversionException. * * - boolean: returns "false" or "true" * - char/short/int/long: return the integer as a string using boost::lexical_cast * - byte/word/dword/qword: return the unsigned integer as a string using boost::lexical_cast * - float/double: return float as a string using boost::lexical_cast * - string: return the string. */ std::string getString() const; /** Return the value converted to a quoted string. This function never * throws ConversionException. It calls getString() and adds " and escape * sequences. */ std::string getStringQuoted() const; // *** Unary Operators /** Unary prefix - operator. Attempts to convert the AnyScalar to a numeric * value. * * - bool: invert the boolean value * - char/short/int/long: switch sign * - byte/word/dword/qword: negated the number even tho it doesn't make sense. * - float/double: switch sign * - string: attempt to convert the string to a double and negate it! * May throw a ConversionException */ AnyScalar operator-() const; // *** Binary Operators // These will convert the two operands to the largest common type of the // same field. #ifndef SWIG // obviously too strange for SWIG private: /** Binary arithmetic template operator. Converts the two AnyScalars into the * largest type of their common field. If a string cannot be converted to a * numeric of the same field as the other operand a ConversionException is * thrown. * Conversion chart: * signifies the operator - bool * (any): ConversionException. Don't consider boolean as the field F_2. - char|short|int * char|short|int: return AnyScalar is an integer. - byte|word|dword * char|short|int: returned AnyScalar is a signed integer. - byte|word|dword * byte|word|dword: returned AnyScalar is an unsigned integer. - long * char|short|int|long|byte|word|dword|qword: returned AnyScalar is a signed long (long). - qword * char|short|int|long: : returned AnyScalar is a signed long (long). - qword * byte|word|dword|qword: : returned AnyScalar is an unsigned long (long). - float|double * char|short|int|long|byte|word|dword|qword: calculate as floating point number - string * char|short|int: convert string to integer, calculate as signed integer. - string * byte|word|dword: convert string to unsigned integer, calculate as unsigned integer. - string * long: convert string to long integer, calculate as long. - string * qword: convert string to unsigned long integer, calculate as unsigned long. - string * float|double: convert string to floating point, calculate as floating point. - string * string: only valid for "+" operator as string concatenation. */ template <template <typename Type> class Operator, char OpName> AnyScalar binary_arith_op(const AnyScalar &b) const; #endif public: /// Instantiation of binary_arith_op for "+" plus. inline AnyScalar operator+(const AnyScalar &b) const { return binary_arith_op<std::plus, '+'>(b); } /// Instantiation of binary_arith_op for "-" minus. inline AnyScalar operator-(const AnyScalar &b) const { return binary_arith_op<std::minus, '-'>(b); } /// Instantiation of binary_arith_op for "*" multiplication. inline AnyScalar operator*(const AnyScalar &b) const { return binary_arith_op<std::multiplies, '*'>(b); } /// Instantiation of binary_arith_op for "/" division. inline AnyScalar operator/(const AnyScalar &b) const { return binary_arith_op<std::divides, '/'>(b); } /// Alias for operator+ in script languages inline AnyScalar add(const AnyScalar &b) const { return (*this + b); } /// Alias for operator- in script languages inline AnyScalar subtract(const AnyScalar &b) const { return (*this - b); } /// Alias for operator* in script languages inline AnyScalar multiply(const AnyScalar &b) const { return (*this * b); } /// Alias for operator/ in script languages inline AnyScalar divide(const AnyScalar &b) const { return (*this / b); } #ifndef SWIG // obviously too strange for SWIG private: /** Binary comparison template operator. Converts the two AnyScalars into the * largest type of their common field. If a string cannot be converted to a * numeric of the same field as the other operand a ConversionException is * thrown. Operator is the STL operator class. OpNum is an identifier for * the operator name string within the template. * Conversion chart: * signifies the operator - bool * bool: usual comparison - bool * (any): ConversionException. Don't consider boolean as the field F_2. - char|short|int * char|short|int: compare as signed integers. - byte|word|dword * char|short|int: compare as signed integers. - byte|word|dword * byte|word|dword: compare as unsigned integers. - long * char|short|int|long|byte|word|dword|qword: compare as signed long integer - qword * char|short|int|long: : compare as signed long integer - qword * byte|word|dword|qword: : compare an unsigned long (long). - float|double * char|short|int|long|byte|word|dword|qword: compare as floating point numbers - string * char|short|int: convert string to integer, compare as signed integer. - string * byte|word|dword: convert string to unsigned integer, compare as unsigned integer. - string * long: convert string to long integer, compare as long. - string * qword: convert string to unsigned long integer, compare as unsigned long. - string * float|double: convert string to floating point, compare as floating point. - string * string: usual string comparison operators. */ template <template <typename Type> class Operator, int OpNum> bool binary_comp_op(const AnyScalar &b) const; #endif // *** Don't use the operators themselves, because operator== is defined // *** differently above. public: /// Instantiation of binary_comp_op for "==" equality. inline bool equal_to(const AnyScalar &b) const { return binary_comp_op<std::equal_to, 0>(b); } /// Instantiation of binary_comp_op for "!=" inequality. inline bool not_equal_to(const AnyScalar &b) const { return binary_comp_op<std::not_equal_to, 1>(b); } /// Instantiation of binary_comp_op for "<" less-than. inline bool less(const AnyScalar &b) const { return binary_comp_op<std::less, 2>(b); } /// Instantiation of binary_comp_op for ">" greater-than. inline bool greater(const AnyScalar &b) const { return binary_comp_op<std::greater, 3>(b); } /// Instantiation of binary_comp_op for "<=" less-or-equal-than. inline bool less_equal(const AnyScalar &b) const { return binary_comp_op<std::less_equal, 4>(b); } /// Instantiation of binary_comp_op for ">=" greater-or-equal-than. inline bool greater_equal(const AnyScalar &b) const { return binary_comp_op<std::greater_equal, 5>(b); } }; /// Make AnyScalar outputtable to ostream static inline std::ostream& operator<< (std::ostream &stream, const AnyScalar &as) { return stream << as.getString(); } } // namespace stx #endif // VGS_AnyScalar_H